mirror of
https://github.com/dbt-labs/dbt-core
synced 2025-12-20 06:11:28 +00:00
Compare commits
7 Commits
enable-pos
...
arky/test-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa6ac01d5a | ||
|
|
072476e5f5 | ||
|
|
a3a2baed59 | ||
|
|
9501e67804 | ||
|
|
6f086c9047 | ||
|
|
9c47d4ec33 | ||
|
|
e0b18a677c |
@@ -1,5 +1,5 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 1.4.0a1
|
current_version = 1.4.0b1
|
||||||
parse = (?P<major>\d+)
|
parse = (?P<major>\d+)
|
||||||
\.(?P<minor>\d+)
|
\.(?P<minor>\d+)
|
||||||
\.(?P<patch>\d+)
|
\.(?P<patch>\d+)
|
||||||
|
|||||||
89
.changes/1.4.0-b1.md
Normal file
89
.changes/1.4.0-b1.md
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
## dbt-core 1.4.0-b1 - December 15, 2022
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Added favor-state flag to optionally favor state nodes even if unselected node exists ([#2968](https://github.com/dbt-labs/dbt-core/issues/2968))
|
||||||
|
- Update structured logging. Convert to using protobuf messages. Ensure events are enriched with node_info. ([#5610](https://github.com/dbt-labs/dbt-core/issues/5610))
|
||||||
|
- Friendlier error messages when packages.yml is malformed ([#5486](https://github.com/dbt-labs/dbt-core/issues/5486))
|
||||||
|
- Migrate dbt-utils current_timestamp macros into core + adapters ([#5521](https://github.com/dbt-labs/dbt-core/issues/5521))
|
||||||
|
- Allow partitions in external tables to be supplied as a list ([#5929](https://github.com/dbt-labs/dbt-core/issues/5929))
|
||||||
|
- extend -f flag shorthand for seed command ([#5990](https://github.com/dbt-labs/dbt-core/issues/5990))
|
||||||
|
- This pulls the profile name from args when constructing a RuntimeConfig in lib.py, enabling the dbt-server to override the value that's in the dbt_project.yml ([#6201](https://github.com/dbt-labs/dbt-core/issues/6201))
|
||||||
|
- Adding tarball install method for packages. Allowing package tarball to be specified via url in the packages.yaml. ([#4205](https://github.com/dbt-labs/dbt-core/issues/4205))
|
||||||
|
- Added an md5 function to the base context ([#6246](https://github.com/dbt-labs/dbt-core/issues/6246))
|
||||||
|
- Exposures support metrics in lineage ([#6057](https://github.com/dbt-labs/dbt-core/issues/6057))
|
||||||
|
- Add support for Python 3.11 ([#6147](https://github.com/dbt-labs/dbt-core/issues/6147))
|
||||||
|
- incremental predicates ([#5680](https://github.com/dbt-labs/dbt-core/issues/5680))
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Account for disabled flags on models in schema files more completely ([#3992](https://github.com/dbt-labs/dbt-core/issues/3992))
|
||||||
|
- Add validation of enabled config for metrics, exposures and sources ([#6030](https://github.com/dbt-labs/dbt-core/issues/6030))
|
||||||
|
- check length of args of python model function before accessing it ([#6041](https://github.com/dbt-labs/dbt-core/issues/6041))
|
||||||
|
- Add functors to ensure event types with str-type attributes are initialized to spec, even when provided non-str type params. ([#5436](https://github.com/dbt-labs/dbt-core/issues/5436))
|
||||||
|
- Allow hooks to fail without halting execution flow ([#5625](https://github.com/dbt-labs/dbt-core/issues/5625))
|
||||||
|
- Clarify Error Message for how many models are allowed in a Python file ([#6245](https://github.com/dbt-labs/dbt-core/issues/6245))
|
||||||
|
- After this, will be possible to use default values for dbt.config.get ([#6309](https://github.com/dbt-labs/dbt-core/issues/6309))
|
||||||
|
- Use full path for writing manifest ([#6055](https://github.com/dbt-labs/dbt-core/issues/6055))
|
||||||
|
- [CT-1284] Change Python model default materialization to table ([#6345](https://github.com/dbt-labs/dbt-core/issues/6345))
|
||||||
|
- Repair a regression which prevented basic logging before the logging subsystem is completely configured. ([#6434](https://github.com/dbt-labs/dbt-core/issues/6434))
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
|
||||||
|
- minor doc correction ([dbt-docs/#5791](https://github.com/dbt-labs/dbt-docs/issues/5791))
|
||||||
|
- Generate API docs for new CLI interface ([dbt-docs/#5528](https://github.com/dbt-labs/dbt-docs/issues/5528))
|
||||||
|
- ([dbt-docs/#5880](https://github.com/dbt-labs/dbt-docs/issues/5880))
|
||||||
|
- Fix rendering of sample code for metrics ([dbt-docs/#323](https://github.com/dbt-labs/dbt-docs/issues/323))
|
||||||
|
- Alphabetize `core/dbt/README.md` ([dbt-docs/#6368](https://github.com/dbt-labs/dbt-docs/issues/6368))
|
||||||
|
|
||||||
|
### Under the Hood
|
||||||
|
|
||||||
|
- Put black config in explicit config ([#5946](https://github.com/dbt-labs/dbt-core/issues/5946))
|
||||||
|
- Added flat_graph attribute the Manifest class's deepcopy() coverage ([#5809](https://github.com/dbt-labs/dbt-core/issues/5809))
|
||||||
|
- Add mypy configs so `mypy` passes from CLI ([#5983](https://github.com/dbt-labs/dbt-core/issues/5983))
|
||||||
|
- Exception message cleanup. ([#6023](https://github.com/dbt-labs/dbt-core/issues/6023))
|
||||||
|
- Add dmypy cache to gitignore ([#6028](https://github.com/dbt-labs/dbt-core/issues/6028))
|
||||||
|
- Provide useful errors when the value of 'materialized' is invalid ([#5229](https://github.com/dbt-labs/dbt-core/issues/5229))
|
||||||
|
- Clean up string formatting ([#6068](https://github.com/dbt-labs/dbt-core/issues/6068))
|
||||||
|
- Fixed extra whitespace in strings introduced by black. ([#1350](https://github.com/dbt-labs/dbt-core/issues/1350))
|
||||||
|
- Remove the 'root_path' field from most nodes ([#6171](https://github.com/dbt-labs/dbt-core/issues/6171))
|
||||||
|
- Combine certain logging events with different levels ([#6173](https://github.com/dbt-labs/dbt-core/issues/6173))
|
||||||
|
- Convert threading tests to pytest ([#5942](https://github.com/dbt-labs/dbt-core/issues/5942))
|
||||||
|
- Convert postgres index tests to pytest ([#5770](https://github.com/dbt-labs/dbt-core/issues/5770))
|
||||||
|
- Convert use color tests to pytest ([#5771](https://github.com/dbt-labs/dbt-core/issues/5771))
|
||||||
|
- Add github actions workflow to generate high level CLI API docs ([#5942](https://github.com/dbt-labs/dbt-core/issues/5942))
|
||||||
|
- Functionality-neutral refactor of event logging system to improve encapsulation and modularity. ([#6139](https://github.com/dbt-labs/dbt-core/issues/6139))
|
||||||
|
- Consolidate ParsedNode and CompiledNode classes ([#6383](https://github.com/dbt-labs/dbt-core/issues/6383))
|
||||||
|
- Prevent doc gen workflow from running on forks ([#6386](https://github.com/dbt-labs/dbt-core/issues/6386))
|
||||||
|
- Fix intermittent database connection failure in Windows CI test ([#6394](https://github.com/dbt-labs/dbt-core/issues/6394))
|
||||||
|
- Refactor and clean up manifest nodes ([#6426](https://github.com/dbt-labs/dbt-core/issues/6426))
|
||||||
|
- Restore important legacy logging behaviors, following refactor which removed them ([#6437](https://github.com/dbt-labs/dbt-core/issues/6437))
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Update pathspec requirement from ~=0.9.0 to >=0.9,<0.11 in /core ([#5917](https://github.com/dbt-labs/dbt-core/pull/5917))
|
||||||
|
- Bump black from 22.8.0 to 22.10.0 ([#6019](https://github.com/dbt-labs/dbt-core/pull/6019))
|
||||||
|
- Bump mashumaro[msgpack] from 3.0.4 to 3.1.1 in /core ([#6108](https://github.com/dbt-labs/dbt-core/pull/6108))
|
||||||
|
- Update colorama requirement from <0.4.6,>=0.3.9 to >=0.3.9,<0.4.7 in /core ([#6144](https://github.com/dbt-labs/dbt-core/pull/6144))
|
||||||
|
- Bump mashumaro[msgpack] from 3.1.1 to 3.2 in /core ([#4904](https://github.com/dbt-labs/dbt-core/issues/4904))
|
||||||
|
|
||||||
|
### Contributors
|
||||||
|
- [@andy-clapson](https://github.com/andy-clapson) ([dbt-docs/#5791](https://github.com/dbt-labs/dbt-docs/issues/5791))
|
||||||
|
- [@chamini2](https://github.com/chamini2) ([#6041](https://github.com/dbt-labs/dbt-core/issues/6041))
|
||||||
|
- [@daniel-murray](https://github.com/daniel-murray) ([#2968](https://github.com/dbt-labs/dbt-core/issues/2968))
|
||||||
|
- [@dave-connors-3](https://github.com/dave-connors-3) ([#5990](https://github.com/dbt-labs/dbt-core/issues/5990))
|
||||||
|
- [@dbeatty10](https://github.com/dbeatty10) ([dbt-docs/#6368](https://github.com/dbt-labs/dbt-docs/issues/6368), [#6394](https://github.com/dbt-labs/dbt-core/issues/6394))
|
||||||
|
- [@devmessias](https://github.com/devmessias) ([#6309](https://github.com/dbt-labs/dbt-core/issues/6309))
|
||||||
|
- [@eve-johns](https://github.com/eve-johns) ([#6068](https://github.com/dbt-labs/dbt-core/issues/6068))
|
||||||
|
- [@haritamar](https://github.com/haritamar) ([#6246](https://github.com/dbt-labs/dbt-core/issues/6246))
|
||||||
|
- [@jared-rimmer](https://github.com/jared-rimmer) ([#5486](https://github.com/dbt-labs/dbt-core/issues/5486))
|
||||||
|
- [@josephberni](https://github.com/josephberni) ([#2968](https://github.com/dbt-labs/dbt-core/issues/2968))
|
||||||
|
- [@joshuataylor](https://github.com/joshuataylor) ([#6147](https://github.com/dbt-labs/dbt-core/issues/6147))
|
||||||
|
- [@justbldwn](https://github.com/justbldwn) ([#6245](https://github.com/dbt-labs/dbt-core/issues/6245))
|
||||||
|
- [@luke-bassett](https://github.com/luke-bassett) ([#1350](https://github.com/dbt-labs/dbt-core/issues/1350))
|
||||||
|
- [@max-sixty](https://github.com/max-sixty) ([#5946](https://github.com/dbt-labs/dbt-core/issues/5946), [#5983](https://github.com/dbt-labs/dbt-core/issues/5983), [#6028](https://github.com/dbt-labs/dbt-core/issues/6028))
|
||||||
|
- [@paulbenschmidt](https://github.com/paulbenschmidt) ([dbt-docs/#5880](https://github.com/dbt-labs/dbt-docs/issues/5880))
|
||||||
|
- [@pgoslatara](https://github.com/pgoslatara) ([#5929](https://github.com/dbt-labs/dbt-core/issues/5929))
|
||||||
|
- [@racheldaniel](https://github.com/racheldaniel) ([#6201](https://github.com/dbt-labs/dbt-core/issues/6201))
|
||||||
|
- [@timle2](https://github.com/timle2) ([#4205](https://github.com/dbt-labs/dbt-core/issues/4205))
|
||||||
|
- [@dave-connors-3](https://github.com/dave-connors-3) ([#5680](https://github.com/dbt-labs/dbt-core/issues/5680))
|
||||||
6
.changes/1.4.0/Dependency-20220923-000646.yaml
Normal file
6
.changes/1.4.0/Dependency-20220923-000646.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: "Dependencies"
|
||||||
|
body: "Update pathspec requirement from ~=0.9.0 to >=0.9,<0.11 in /core"
|
||||||
|
time: 2022-09-23T00:06:46.00000Z
|
||||||
|
custom:
|
||||||
|
Author: dependabot[bot]
|
||||||
|
PR: "5917"
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
kind: "Dependency"
|
kind: "Dependencies"
|
||||||
body: "Bump black from 22.8.0 to 22.10.0"
|
body: "Bump black from 22.8.0 to 22.10.0"
|
||||||
time: 2022-10-07T00:08:48.00000Z
|
time: 2022-10-07T00:08:48.00000Z
|
||||||
custom:
|
custom:
|
||||||
Author: dependabot[bot]
|
Author: dependabot[bot]
|
||||||
Issue: 4904
|
PR: "6019"
|
||||||
PR: 6019
|
|
||||||
6
.changes/1.4.0/Dependency-20221020-000753.yaml
Normal file
6
.changes/1.4.0/Dependency-20221020-000753.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: "Dependencies"
|
||||||
|
body: "Bump mashumaro[msgpack] from 3.0.4 to 3.1.1 in /core"
|
||||||
|
time: 2022-10-20T00:07:53.00000Z
|
||||||
|
custom:
|
||||||
|
Author: dependabot[bot]
|
||||||
|
PR: "6108"
|
||||||
6
.changes/1.4.0/Dependency-20221026-000910.yaml
Normal file
6
.changes/1.4.0/Dependency-20221026-000910.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: "Dependencies"
|
||||||
|
body: "Update colorama requirement from <0.4.6,>=0.3.9 to >=0.3.9,<0.4.7 in /core"
|
||||||
|
time: 2022-10-26T00:09:10.00000Z
|
||||||
|
custom:
|
||||||
|
Author: dependabot[bot]
|
||||||
|
PR: "6144"
|
||||||
7
.changes/1.4.0/Dependency-20221205-002118.yaml
Normal file
7
.changes/1.4.0/Dependency-20221205-002118.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: "Dependencies"
|
||||||
|
body: "Bump mashumaro[msgpack] from 3.1.1 to 3.2 in /core"
|
||||||
|
time: 2022-12-05T00:21:18.00000Z
|
||||||
|
custom:
|
||||||
|
Author: dependabot[bot]
|
||||||
|
Issue: 4904
|
||||||
|
PR: 6375
|
||||||
@@ -4,4 +4,3 @@ time: 2022-09-08T15:41:57.689162-04:00
|
|||||||
custom:
|
custom:
|
||||||
Author: andy-clapson
|
Author: andy-clapson
|
||||||
Issue: "5791"
|
Issue: "5791"
|
||||||
PR: "5684"
|
|
||||||
@@ -4,4 +4,3 @@ time: 2022-10-07T09:06:56.446078-05:00
|
|||||||
custom:
|
custom:
|
||||||
Author: stu-k
|
Author: stu-k
|
||||||
Issue: "5528"
|
Issue: "5528"
|
||||||
PR: "6022"
|
|
||||||
5
.changes/1.4.0/Docs-20221017-171411.yaml
Normal file
5
.changes/1.4.0/Docs-20221017-171411.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
kind: Docs
|
||||||
|
time: 2022-10-17T17:14:11.715348-05:00
|
||||||
|
custom:
|
||||||
|
Author: paulbenschmidt
|
||||||
|
Issue: "5880"
|
||||||
6
.changes/1.4.0/Docs-20221116-155743.yaml
Normal file
6
.changes/1.4.0/Docs-20221116-155743.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Docs
|
||||||
|
body: Fix rendering of sample code for metrics
|
||||||
|
time: 2022-11-16T15:57:43.204201+01:00
|
||||||
|
custom:
|
||||||
|
Author: jtcohen6
|
||||||
|
Issue: "323"
|
||||||
6
.changes/1.4.0/Docs-20221202-150523.yaml
Normal file
6
.changes/1.4.0/Docs-20221202-150523.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Docs
|
||||||
|
body: Alphabetize `core/dbt/README.md`
|
||||||
|
time: 2022-12-02T15:05:23.695333-07:00
|
||||||
|
custom:
|
||||||
|
Author: dbeatty10
|
||||||
|
Issue: "6368"
|
||||||
7
.changes/1.4.0/Features-20220408-165459.yaml
Normal file
7
.changes/1.4.0/Features-20220408-165459.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: Features
|
||||||
|
body: Added favor-state flag to optionally favor state nodes even if unselected node
|
||||||
|
exists
|
||||||
|
time: 2022-04-08T16:54:59.696564+01:00
|
||||||
|
custom:
|
||||||
|
Author: daniel-murray josephberni
|
||||||
|
Issue: "2968"
|
||||||
6
.changes/1.4.0/Features-20220817-154857.yaml
Normal file
6
.changes/1.4.0/Features-20220817-154857.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Features
|
||||||
|
body: Update structured logging. Convert to using protobuf messages. Ensure events are enriched with node_info.
|
||||||
|
time: 2022-08-17T15:48:57.225267-04:00
|
||||||
|
custom:
|
||||||
|
Author: gshank
|
||||||
|
Issue: "5610"
|
||||||
7
.changes/1.4.0/Features-20220823-085727.yaml
Normal file
7
.changes/1.4.0/Features-20220823-085727.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: Features
|
||||||
|
body: incremental predicates
|
||||||
|
time: 2022-08-23T08:57:27.640804-05:00
|
||||||
|
custom:
|
||||||
|
Author: dave-connors-3
|
||||||
|
Issue: "5680"
|
||||||
|
PR: "5702"
|
||||||
@@ -4,4 +4,3 @@ time: 2022-09-12T12:59:35.121188+01:00
|
|||||||
custom:
|
custom:
|
||||||
Author: jared-rimmer
|
Author: jared-rimmer
|
||||||
Issue: "5486"
|
Issue: "5486"
|
||||||
PR: "5812"
|
|
||||||
@@ -4,4 +4,3 @@ time: 2022-09-14T09:56:25.97818-07:00
|
|||||||
custom:
|
custom:
|
||||||
Author: colin-rogers-dbt
|
Author: colin-rogers-dbt
|
||||||
Issue: "5521"
|
Issue: "5521"
|
||||||
PR: "5838"
|
|
||||||
@@ -4,4 +4,3 @@ time: 2022-09-25T21:16:51.051239654+02:00
|
|||||||
custom:
|
custom:
|
||||||
Author: pgoslatara
|
Author: pgoslatara
|
||||||
Issue: "5929"
|
Issue: "5929"
|
||||||
PR: "5930"
|
|
||||||
@@ -4,4 +4,3 @@ time: 2022-10-03T11:07:05.381632-05:00
|
|||||||
custom:
|
custom:
|
||||||
Author: dave-connors-3
|
Author: dave-connors-3
|
||||||
Issue: "5990"
|
Issue: "5990"
|
||||||
PR: "5991"
|
|
||||||
7
.changes/1.4.0/Features-20221102-150003.yaml
Normal file
7
.changes/1.4.0/Features-20221102-150003.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: Features
|
||||||
|
body: This pulls the profile name from args when constructing a RuntimeConfig in lib.py,
|
||||||
|
enabling the dbt-server to override the value that's in the dbt_project.yml
|
||||||
|
time: 2022-11-02T15:00:03.000805-05:00
|
||||||
|
custom:
|
||||||
|
Author: racheldaniel
|
||||||
|
Issue: "6201"
|
||||||
8
.changes/1.4.0/Features-20221107-105018.yaml
Normal file
8
.changes/1.4.0/Features-20221107-105018.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
kind: Features
|
||||||
|
body: Adding tarball install method for packages. Allowing package tarball to be specified
|
||||||
|
via url in the packages.yaml.
|
||||||
|
time: 2022-11-07T10:50:18.464545-05:00
|
||||||
|
custom:
|
||||||
|
Author: timle2
|
||||||
|
Issue: "4205"
|
||||||
|
PR: "4689"
|
||||||
6
.changes/1.4.0/Features-20221114-185207.yaml
Normal file
6
.changes/1.4.0/Features-20221114-185207.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Features
|
||||||
|
body: Added an md5 function to the base context
|
||||||
|
time: 2022-11-14T18:52:07.788593+02:00
|
||||||
|
custom:
|
||||||
|
Author: haritamar
|
||||||
|
Issue: "6246"
|
||||||
6
.changes/1.4.0/Features-20221130-112913.yaml
Normal file
6
.changes/1.4.0/Features-20221130-112913.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Features
|
||||||
|
body: Exposures support metrics in lineage
|
||||||
|
time: 2022-11-30T11:29:13.256034-05:00
|
||||||
|
custom:
|
||||||
|
Author: michelleark
|
||||||
|
Issue: "6057"
|
||||||
7
.changes/1.4.0/Features-20221206-150704.yaml
Normal file
7
.changes/1.4.0/Features-20221206-150704.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: Features
|
||||||
|
body: Add support for Python 3.11
|
||||||
|
time: 2022-12-06T15:07:04.753127+01:00
|
||||||
|
custom:
|
||||||
|
Author: joshuataylor MichelleArk jtcohen6
|
||||||
|
Issue: "6147"
|
||||||
|
PR: "6326"
|
||||||
@@ -4,4 +4,3 @@ time: 2022-09-16T10:48:54.162273-05:00
|
|||||||
custom:
|
custom:
|
||||||
Author: emmyoop
|
Author: emmyoop
|
||||||
Issue: "3992"
|
Issue: "3992"
|
||||||
PR: "5868"
|
|
||||||
@@ -4,4 +4,3 @@ time: 2022-10-10T11:32:18.752322-05:00
|
|||||||
custom:
|
custom:
|
||||||
Author: emmyoop
|
Author: emmyoop
|
||||||
Issue: "6030"
|
Issue: "6030"
|
||||||
PR: "6038"
|
|
||||||
@@ -4,4 +4,3 @@ time: 2022-10-11T16:07:15.464093-04:00
|
|||||||
custom:
|
custom:
|
||||||
Author: chamini2
|
Author: chamini2
|
||||||
Issue: "6041"
|
Issue: "6041"
|
||||||
PR: "6042"
|
|
||||||
7
.changes/1.4.0/Fixes-20221016-173742.yaml
Normal file
7
.changes/1.4.0/Fixes-20221016-173742.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: Fixes
|
||||||
|
body: Add functors to ensure event types with str-type attributes are initialized
|
||||||
|
to spec, even when provided non-str type params.
|
||||||
|
time: 2022-10-16T17:37:42.846683-07:00
|
||||||
|
custom:
|
||||||
|
Author: versusfacit
|
||||||
|
Issue: "5436"
|
||||||
6
.changes/1.4.0/Fixes-20221107-095314.yaml
Normal file
6
.changes/1.4.0/Fixes-20221107-095314.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Fixes
|
||||||
|
body: Allow hooks to fail without halting execution flow
|
||||||
|
time: 2022-11-07T09:53:14.340257-06:00
|
||||||
|
custom:
|
||||||
|
Author: ChenyuLInx
|
||||||
|
Issue: "5625"
|
||||||
6
.changes/1.4.0/Fixes-20221115-081021.yaml
Normal file
6
.changes/1.4.0/Fixes-20221115-081021.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Fixes
|
||||||
|
body: Clarify Error Message for how many models are allowed in a Python file
|
||||||
|
time: 2022-11-15T08:10:21.527884-05:00
|
||||||
|
custom:
|
||||||
|
Author: justbldwn
|
||||||
|
Issue: "6245"
|
||||||
7
.changes/1.4.0/Fixes-20221124-163419.yaml
Normal file
7
.changes/1.4.0/Fixes-20221124-163419.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: Fixes
|
||||||
|
body: After this, will be possible to use default values for dbt.config.get
|
||||||
|
time: 2022-11-24T16:34:19.039512764-03:00
|
||||||
|
custom:
|
||||||
|
Author: devmessias
|
||||||
|
Issue: "6309"
|
||||||
|
PR: "6317"
|
||||||
6
.changes/1.4.0/Fixes-20221202-164859.yaml
Normal file
6
.changes/1.4.0/Fixes-20221202-164859.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Fixes
|
||||||
|
body: Use full path for writing manifest
|
||||||
|
time: 2022-12-02T16:48:59.029519-05:00
|
||||||
|
custom:
|
||||||
|
Author: gshank
|
||||||
|
Issue: "6055"
|
||||||
6
.changes/1.4.0/Fixes-20221213-112620.yaml
Normal file
6
.changes/1.4.0/Fixes-20221213-112620.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Fixes
|
||||||
|
body: '[CT-1284] Change Python model default materialization to table'
|
||||||
|
time: 2022-12-13T11:26:20.550017-08:00
|
||||||
|
custom:
|
||||||
|
Author: aranke
|
||||||
|
Issue: "6345"
|
||||||
7
.changes/1.4.0/Fixes-20221214-155307.yaml
Normal file
7
.changes/1.4.0/Fixes-20221214-155307.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: Fixes
|
||||||
|
body: Repair a regression which prevented basic logging before the logging subsystem
|
||||||
|
is completely configured.
|
||||||
|
time: 2022-12-14T15:53:07.396512-05:00
|
||||||
|
custom:
|
||||||
|
Author: peterallenwebb
|
||||||
|
Issue: "6434"
|
||||||
@@ -4,4 +4,3 @@ time: 2022-09-27T19:42:59.241433-07:00
|
|||||||
custom:
|
custom:
|
||||||
Author: max-sixty
|
Author: max-sixty
|
||||||
Issue: "5946"
|
Issue: "5946"
|
||||||
PR: "5947"
|
|
||||||
@@ -4,4 +4,3 @@ time: 2022-09-29T13:44:06.275941-04:00
|
|||||||
custom:
|
custom:
|
||||||
Author: peterallenwebb
|
Author: peterallenwebb
|
||||||
Issue: "5809"
|
Issue: "5809"
|
||||||
PR: "5975"
|
|
||||||
@@ -4,4 +4,3 @@ time: 2022-10-05T12:03:10.061263-07:00
|
|||||||
custom:
|
custom:
|
||||||
Author: max-sixty
|
Author: max-sixty
|
||||||
Issue: "5983"
|
Issue: "5983"
|
||||||
PR: "5983"
|
|
||||||
@@ -4,4 +4,3 @@ time: 2022-10-07T09:46:27.682872-05:00
|
|||||||
custom:
|
custom:
|
||||||
Author: emmyoop
|
Author: emmyoop
|
||||||
Issue: "6023"
|
Issue: "6023"
|
||||||
PR: "6024"
|
|
||||||
@@ -4,4 +4,3 @@ time: 2022-10-07T14:00:44.227644-07:00
|
|||||||
custom:
|
custom:
|
||||||
Author: max-sixty
|
Author: max-sixty
|
||||||
Issue: "6028"
|
Issue: "6028"
|
||||||
PR: "5978"
|
|
||||||
@@ -4,4 +4,3 @@ time: 2022-10-13T18:19:12.167548-04:00
|
|||||||
custom:
|
custom:
|
||||||
Author: peterallenwebb
|
Author: peterallenwebb
|
||||||
Issue: "5229"
|
Issue: "5229"
|
||||||
PR: "6025"
|
|
||||||
6
.changes/1.4.0/Under the Hood-20221017-151511.yaml
Normal file
6
.changes/1.4.0/Under the Hood-20221017-151511.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Under the Hood
|
||||||
|
body: Fixed extra whitespace in strings introduced by black.
|
||||||
|
time: 2022-10-17T15:15:11.499246-05:00
|
||||||
|
custom:
|
||||||
|
Author: luke-bassett
|
||||||
|
Issue: "1350"
|
||||||
6
.changes/1.4.0/Under the Hood-20221017-155844.yaml
Normal file
6
.changes/1.4.0/Under the Hood-20221017-155844.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Under the Hood
|
||||||
|
body: Clean up string formatting
|
||||||
|
time: 2022-10-17T15:58:44.676549-04:00
|
||||||
|
custom:
|
||||||
|
Author: eve-johns
|
||||||
|
Issue: "6068"
|
||||||
6
.changes/1.4.0/Under the Hood-20221028-104837.yaml
Normal file
6
.changes/1.4.0/Under the Hood-20221028-104837.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Under the Hood
|
||||||
|
body: Remove the 'root_path' field from most nodes
|
||||||
|
time: 2022-10-28T10:48:37.687886-04:00
|
||||||
|
custom:
|
||||||
|
Author: gshank
|
||||||
|
Issue: "6171"
|
||||||
6
.changes/1.4.0/Under the Hood-20221028-110344.yaml
Normal file
6
.changes/1.4.0/Under the Hood-20221028-110344.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Under the Hood
|
||||||
|
body: Combine certain logging events with different levels
|
||||||
|
time: 2022-10-28T11:03:44.887836-04:00
|
||||||
|
custom:
|
||||||
|
Author: gshank
|
||||||
|
Issue: "6173"
|
||||||
6
.changes/1.4.0/Under the Hood-20221108-074550.yaml
Normal file
6
.changes/1.4.0/Under the Hood-20221108-074550.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Under the Hood
|
||||||
|
body: Convert threading tests to pytest
|
||||||
|
time: 2022-11-08T07:45:50.589147-06:00
|
||||||
|
custom:
|
||||||
|
Author: stu-k
|
||||||
|
Issue: "5942"
|
||||||
6
.changes/1.4.0/Under the Hood-20221108-115633.yaml
Normal file
6
.changes/1.4.0/Under the Hood-20221108-115633.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Under the Hood
|
||||||
|
body: Convert postgres index tests to pytest
|
||||||
|
time: 2022-11-08T11:56:33.743042-06:00
|
||||||
|
custom:
|
||||||
|
Author: stu-k
|
||||||
|
Issue: "5770"
|
||||||
6
.changes/1.4.0/Under the Hood-20221108-133104.yaml
Normal file
6
.changes/1.4.0/Under the Hood-20221108-133104.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Under the Hood
|
||||||
|
body: Convert use color tests to pytest
|
||||||
|
time: 2022-11-08T13:31:04.788547-06:00
|
||||||
|
custom:
|
||||||
|
Author: stu-k
|
||||||
|
Issue: "5771"
|
||||||
6
.changes/1.4.0/Under the Hood-20221116-130037.yaml
Normal file
6
.changes/1.4.0/Under the Hood-20221116-130037.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Under the Hood
|
||||||
|
body: Add github actions workflow to generate high level CLI API docs
|
||||||
|
time: 2022-11-16T13:00:37.916202-06:00
|
||||||
|
custom:
|
||||||
|
Author: stu-k
|
||||||
|
Issue: "5942"
|
||||||
8
.changes/1.4.0/Under the Hood-20221118-145717.yaml
Normal file
8
.changes/1.4.0/Under the Hood-20221118-145717.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
kind: Under the Hood
|
||||||
|
body: Functionality-neutral refactor of event logging system to improve encapsulation
|
||||||
|
and modularity.
|
||||||
|
time: 2022-11-18T14:57:17.792622-05:00
|
||||||
|
custom:
|
||||||
|
Author: peterallenwebb
|
||||||
|
Issue: "6139"
|
||||||
|
PR: "6291"
|
||||||
7
.changes/1.4.0/Under the Hood-20221205-164948.yaml
Normal file
7
.changes/1.4.0/Under the Hood-20221205-164948.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: Under the Hood
|
||||||
|
body: Consolidate ParsedNode and CompiledNode classes
|
||||||
|
time: 2022-12-05T16:49:48.563583-05:00
|
||||||
|
custom:
|
||||||
|
Author: gshank
|
||||||
|
Issue: "6383"
|
||||||
|
PR: "6384"
|
||||||
7
.changes/1.4.0/Under the Hood-20221206-094015.yaml
Normal file
7
.changes/1.4.0/Under the Hood-20221206-094015.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: Under the Hood
|
||||||
|
body: Prevent doc gen workflow from running on forks
|
||||||
|
time: 2022-12-06T09:40:15.301984-06:00
|
||||||
|
custom:
|
||||||
|
Author: stu-k
|
||||||
|
Issue: "6386"
|
||||||
|
PR: "6390"
|
||||||
7
.changes/1.4.0/Under the Hood-20221206-113053.yaml
Normal file
7
.changes/1.4.0/Under the Hood-20221206-113053.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: Under the Hood
|
||||||
|
body: Fix intermittent database connection failure in Windows CI test
|
||||||
|
time: 2022-12-06T11:30:53.166009-07:00
|
||||||
|
custom:
|
||||||
|
Author: MichelleArk dbeatty10
|
||||||
|
Issue: "6394"
|
||||||
|
PR: "6395"
|
||||||
7
.changes/1.4.0/Under the Hood-20221211-214240.yaml
Normal file
7
.changes/1.4.0/Under the Hood-20221211-214240.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: Under the Hood
|
||||||
|
body: Refactor and clean up manifest nodes
|
||||||
|
time: 2022-12-11T21:42:40.560074-05:00
|
||||||
|
custom:
|
||||||
|
Author: gshank
|
||||||
|
Issue: "6426"
|
||||||
|
PR: "6427"
|
||||||
7
.changes/1.4.0/Under the Hood-20221213-214106.yaml
Normal file
7
.changes/1.4.0/Under the Hood-20221213-214106.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: Under the Hood
|
||||||
|
body: Restore important legacy logging behaviors, following refactor which removed
|
||||||
|
them
|
||||||
|
time: 2022-12-13T21:41:06.815133-05:00
|
||||||
|
custom:
|
||||||
|
Author: peterallenwebb
|
||||||
|
Issue: "6437"
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
kind: Breaking Changes
|
||||||
|
body: Cleaned up exceptions to directly raise in code. Removed use of all exception
|
||||||
|
functions in the code base and marked them all as deprecated to be removed next
|
||||||
|
minor release.
|
||||||
|
time: 2022-12-05T14:19:37.863032-06:00
|
||||||
|
custom:
|
||||||
|
Author: emmyoop
|
||||||
|
Issue: "6339"
|
||||||
|
PR: "6347"
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
kind: Features
|
|
||||||
body: Proto logging messages
|
|
||||||
time: 2022-08-17T15:48:57.225267-04:00
|
|
||||||
custom:
|
|
||||||
Author: gshank
|
|
||||||
Issue: "5610"
|
|
||||||
PR: "5643"
|
|
||||||
7
.changes/unreleased/Fixes-20221117-220320.yaml
Normal file
7
.changes/unreleased/Fixes-20221117-220320.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: Fixes
|
||||||
|
body: Fix typo in util.py
|
||||||
|
time: 2022-11-17T22:03:20.4836855+09:00
|
||||||
|
custom:
|
||||||
|
Author: eltociear
|
||||||
|
Issue: "4904"
|
||||||
|
PR: "6037"
|
||||||
6
.changes/unreleased/Fixes-20221213-113915.yaml
Normal file
6
.changes/unreleased/Fixes-20221213-113915.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Fixes
|
||||||
|
body: '[CT-1591] Don''t parse empty Python files'
|
||||||
|
time: 2022-12-13T11:39:15.818464-08:00
|
||||||
|
custom:
|
||||||
|
Author: aranke
|
||||||
|
Issue: "6345"
|
||||||
6
.changes/unreleased/Under the Hood-20221219-193435.yaml
Normal file
6
.changes/unreleased/Under the Hood-20221219-193435.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Under the Hood
|
||||||
|
body: Treat dense text blobs as binary for `git grep`
|
||||||
|
time: 2022-12-19T19:34:35.890275-07:00
|
||||||
|
custom:
|
||||||
|
Author: dbeatty10
|
||||||
|
Issue: "6294"
|
||||||
@@ -6,19 +6,67 @@ changelogPath: CHANGELOG.md
|
|||||||
versionExt: md
|
versionExt: md
|
||||||
versionFormat: '## dbt-core {{.Version}} - {{.Time.Format "January 02, 2006"}}'
|
versionFormat: '## dbt-core {{.Version}} - {{.Time.Format "January 02, 2006"}}'
|
||||||
kindFormat: '### {{.Kind}}'
|
kindFormat: '### {{.Kind}}'
|
||||||
changeFormat: '- {{.Body}} ([#{{.Custom.Issue}}](https://github.com/dbt-labs/dbt-core/issues/{{.Custom.Issue}}), [#{{.Custom.PR}}](https://github.com/dbt-labs/dbt-core/pull/{{.Custom.PR}}))'
|
changeFormat: |-
|
||||||
|
{{- $IssueList := list }}
|
||||||
|
{{- $changes := splitList " " $.Custom.Issue }}
|
||||||
|
{{- range $issueNbr := $changes }}
|
||||||
|
{{- $changeLink := "[#nbr](https://github.com/dbt-labs/dbt-core/issues/nbr)" | replace "nbr" $issueNbr }}
|
||||||
|
{{- $IssueList = append $IssueList $changeLink }}
|
||||||
|
{{- end -}}
|
||||||
|
- {{.Body}} ({{ range $index, $element := $IssueList }}{{if $index}}, {{end}}{{$element}}{{end}})
|
||||||
|
|
||||||
kinds:
|
kinds:
|
||||||
- label: Breaking Changes
|
- label: Breaking Changes
|
||||||
- label: Features
|
- label: Features
|
||||||
- label: Fixes
|
- label: Fixes
|
||||||
- label: Docs
|
- label: Docs
|
||||||
changeFormat: '- {{.Body}} ([dbt-docs/#{{.Custom.Issue}}](https://github.com/dbt-labs/dbt-docs/issues/{{.Custom.Issue}}), [dbt-docs/#{{.Custom.PR}}](https://github.com/dbt-labs/dbt-docs/pull/{{.Custom.PR}}))'
|
changeFormat: |-
|
||||||
|
{{- $IssueList := list }}
|
||||||
|
{{- $changes := splitList " " $.Custom.Issue }}
|
||||||
|
{{- range $issueNbr := $changes }}
|
||||||
|
{{- $changeLink := "[dbt-docs/#nbr](https://github.com/dbt-labs/dbt-docs/issues/nbr)" | replace "nbr" $issueNbr }}
|
||||||
|
{{- $IssueList = append $IssueList $changeLink }}
|
||||||
|
{{- end -}}
|
||||||
|
- {{.Body}} ({{ range $index, $element := $IssueList }}{{if $index}}, {{end}}{{$element}}{{end}})
|
||||||
- label: Under the Hood
|
- label: Under the Hood
|
||||||
- label: Dependencies
|
- label: Dependencies
|
||||||
changeFormat: '- {{.Body}} ({{if ne .Custom.Issue ""}}[#{{.Custom.Issue}}](https://github.com/dbt-labs/dbt-core/issues/{{.Custom.Issue}}), {{end}}[#{{.Custom.PR}}](https://github.com/dbt-labs/dbt-core/pull/{{.Custom.PR}}))'
|
changeFormat: |-
|
||||||
|
{{- $PRList := list }}
|
||||||
|
{{- $changes := splitList " " $.Custom.PR }}
|
||||||
|
{{- range $pullrequest := $changes }}
|
||||||
|
{{- $changeLink := "[#nbr](https://github.com/dbt-labs/dbt-core/pull/nbr)" | replace "nbr" $pullrequest }}
|
||||||
|
{{- $PRList = append $PRList $changeLink }}
|
||||||
|
{{- end -}}
|
||||||
|
- {{.Body}} ({{ range $index, $element := $PRList }}{{if $index}}, {{end}}{{$element}}{{end}})
|
||||||
|
skipGlobalChoices: true
|
||||||
|
additionalChoices:
|
||||||
|
- key: Author
|
||||||
|
label: GitHub Username(s) (separated by a single space if multiple)
|
||||||
|
type: string
|
||||||
|
minLength: 3
|
||||||
|
- key: PR
|
||||||
|
label: GitHub Pull Request Number (separated by a single space if multiple)
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
- label: Security
|
- label: Security
|
||||||
changeFormat: '- {{.Body}} ({{if ne .Custom.Issue ""}}[#{{.Custom.Issue}}](https://github.com/dbt-labs/dbt-core/issues/{{.Custom.Issue}}), {{end}}[#{{.Custom.PR}}](https://github.com/dbt-labs/dbt-core/pull/{{.Custom.PR}}))'
|
changeFormat: |-
|
||||||
|
{{- $PRList := list }}
|
||||||
|
{{- $changes := splitList " " $.Custom.PR }}
|
||||||
|
{{- range $pullrequest := $changes }}
|
||||||
|
{{- $changeLink := "[#nbr](https://github.com/dbt-labs/dbt-core/pull/nbr)" | replace "nbr" $pullrequest }}
|
||||||
|
{{- $PRList = append $PRList $changeLink }}
|
||||||
|
{{- end -}}
|
||||||
|
- {{.Body}} ({{ range $index, $element := $PRList }}{{if $index}}, {{end}}{{$element}}{{end}})
|
||||||
|
skipGlobalChoices: true
|
||||||
|
additionalChoices:
|
||||||
|
- key: Author
|
||||||
|
label: GitHub Username(s) (separated by a single space if multiple)
|
||||||
|
type: string
|
||||||
|
minLength: 3
|
||||||
|
- key: PR
|
||||||
|
label: GitHub Pull Request Number (separated by a single space if multiple)
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
|
|
||||||
newlines:
|
newlines:
|
||||||
afterChangelogHeader: 1
|
afterChangelogHeader: 1
|
||||||
@@ -33,40 +81,39 @@ custom:
|
|||||||
type: string
|
type: string
|
||||||
minLength: 3
|
minLength: 3
|
||||||
- key: Issue
|
- key: Issue
|
||||||
label: GitHub Issue Number
|
label: GitHub Issue Number (separated by a single space if multiple)
|
||||||
type: int
|
type: string
|
||||||
minInt: 1
|
minLength: 1
|
||||||
- key: PR
|
|
||||||
label: GitHub Pull Request Number
|
|
||||||
type: int
|
|
||||||
minInt: 1
|
|
||||||
|
|
||||||
footerFormat: |
|
footerFormat: |
|
||||||
{{- $contributorDict := dict }}
|
{{- $contributorDict := dict }}
|
||||||
{{- /* any names added to this list should be all lowercase for later matching purposes */}}
|
{{- /* any names added to this list should be all lowercase for later matching purposes */}}
|
||||||
{{- $core_team := list "michelleark" "peterallenwebb" "emmyoop" "nathaniel-may" "gshank" "leahwicz" "chenyulinx" "stu-k" "iknox-fa" "versusfacit" "mcknight-42" "jtcohen6" "dependabot[bot]" "snyk-bot" "colin-rogers-dbt" }}
|
{{- $core_team := list "michelleark" "peterallenwebb" "emmyoop" "nathaniel-may" "gshank" "leahwicz" "chenyulinx" "stu-k" "iknox-fa" "versusfacit" "mcknight-42" "jtcohen6" "aranke" "dependabot[bot]" "snyk-bot" "colin-rogers-dbt" }}
|
||||||
{{- range $change := .Changes }}
|
{{- range $change := .Changes }}
|
||||||
{{- $authorList := splitList " " $change.Custom.Author }}
|
{{- $authorList := splitList " " $change.Custom.Author }}
|
||||||
{{- /* loop through all authors for a PR */}}
|
{{- /* loop through all authors for a single changelog */}}
|
||||||
{{- range $author := $authorList }}
|
{{- range $author := $authorList }}
|
||||||
{{- $authorLower := lower $author }}
|
{{- $authorLower := lower $author }}
|
||||||
{{- /* we only want to include non-core team contributors */}}
|
{{- /* we only want to include non-core team contributors */}}
|
||||||
{{- if not (has $authorLower $core_team)}}
|
{{- if not (has $authorLower $core_team)}}
|
||||||
{{- /* Docs kind link back to dbt-docs instead of dbt-core PRs */}}
|
{{- $changeList := splitList " " $change.Custom.Author }}
|
||||||
{{- $prLink := $change.Kind }}
|
{{- /* Docs kind link back to dbt-docs instead of dbt-core issues */}}
|
||||||
{{- if eq $change.Kind "Docs" }}
|
{{- $changeLink := $change.Kind }}
|
||||||
{{- $prLink = "[dbt-docs/#pr](https://github.com/dbt-labs/dbt-docs/pull/pr)" | replace "pr" $change.Custom.PR }}
|
{{- if or (eq $change.Kind "Dependencies") (eq $change.Kind "Security") }}
|
||||||
|
{{- $changeLink = "[#nbr](https://github.com/dbt-labs/dbt-core/pull/nbr)" | replace "nbr" $change.Custom.PR }}
|
||||||
|
{{- else if eq $change.Kind "Docs"}}
|
||||||
|
{{- $changeLink = "[dbt-docs/#nbr](https://github.com/dbt-labs/dbt-docs/issues/nbr)" | replace "nbr" $change.Custom.Issue }}
|
||||||
{{- else }}
|
{{- else }}
|
||||||
{{- $prLink = "[#pr](https://github.com/dbt-labs/dbt-core/pull/pr)" | replace "pr" $change.Custom.PR }}
|
{{- $changeLink = "[#nbr](https://github.com/dbt-labs/dbt-core/issues/nbr)" | replace "nbr" $change.Custom.Issue }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- /* check if this contributor has other PRs associated with them already */}}
|
{{- /* check if this contributor has other changes associated with them already */}}
|
||||||
{{- if hasKey $contributorDict $author }}
|
{{- if hasKey $contributorDict $author }}
|
||||||
{{- $prList := get $contributorDict $author }}
|
{{- $contributionList := get $contributorDict $author }}
|
||||||
{{- $prList = append $prList $prLink }}
|
{{- $contributionList = append $contributionList $changeLink }}
|
||||||
{{- $contributorDict := set $contributorDict $author $prList }}
|
{{- $contributorDict := set $contributorDict $author $contributionList }}
|
||||||
{{- else }}
|
{{- else }}
|
||||||
{{- $prList := list $prLink }}
|
{{- $contributionList := list $changeLink }}
|
||||||
{{- $contributorDict := set $contributorDict $author $prList }}
|
{{- $contributorDict := set $contributorDict $author $contributionList }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
|
|||||||
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
core/dbt/include/index.html binary
|
||||||
|
tests/functional/artifacts/data/state/*/manifest.json binary
|
||||||
4
.github/workflows/bot-changelog.yml
vendored
4
.github/workflows/bot-changelog.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- label: "dependencies"
|
- label: "dependencies"
|
||||||
changie_kind: "Dependency"
|
changie_kind: "Dependencies"
|
||||||
- label: "snyk"
|
- label: "snyk"
|
||||||
changie_kind: "Security"
|
changie_kind: "Security"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -58,4 +58,4 @@ jobs:
|
|||||||
commit_message: "Add automated changelog yaml from template for bot PR"
|
commit_message: "Add automated changelog yaml from template for bot PR"
|
||||||
changie_kind: ${{ matrix.changie_kind }}
|
changie_kind: ${{ matrix.changie_kind }}
|
||||||
label: ${{ matrix.label }}
|
label: ${{ matrix.label }}
|
||||||
custom_changelog_string: "custom:\n Author: ${{ github.event.pull_request.user.login }}\n Issue: 4904\n PR: ${{ github.event.pull_request.number }}"
|
custom_changelog_string: "custom:\n Author: ${{ github.event.pull_request.user.login }}\n PR: ${{ github.event.pull_request.number }}"
|
||||||
|
|||||||
165
.github/workflows/generate-cli-api-docs.yml
vendored
Normal file
165
.github/workflows/generate-cli-api-docs.yml
vendored
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
# **what?**
|
||||||
|
# On push, if anything in core/dbt/docs or core/dbt/cli has been
|
||||||
|
# created or modified, regenerate the CLI API docs using sphinx.
|
||||||
|
|
||||||
|
# **why?**
|
||||||
|
# We watch for changes in core/dbt/cli because the CLI API docs rely on click
|
||||||
|
# and all supporting flags/params to be generated. We watch for changes in
|
||||||
|
# core/dbt/docs since any changes to sphinx configuration or any of the
|
||||||
|
# .rst files there could result in a differently build final index.html file.
|
||||||
|
|
||||||
|
# **when?**
|
||||||
|
# Whenever a change has been pushed to a branch, and only if there is a diff
|
||||||
|
# between the PR branch and main's core/dbt/cli and or core/dbt/docs dirs.
|
||||||
|
|
||||||
|
# TODO: add bot comment to PR informing contributor that the docs have been committed
|
||||||
|
# TODO: figure out why github action triggered pushes cause github to fail to report
|
||||||
|
# the status of jobs
|
||||||
|
|
||||||
|
name: Generate CLI API docs
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
CLI_DIR: ${{ github.workspace }}/core/dbt/cli
|
||||||
|
DOCS_DIR: ${{ github.workspace }}/core/dbt/docs
|
||||||
|
DOCS_BUILD_DIR: ${{ github.workspace }}/core/dbt/docs/build
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check_gen:
|
||||||
|
name: check if generation needed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event.pull_request.head.repo.fork == false }}
|
||||||
|
outputs:
|
||||||
|
cli_dir_changed: ${{ steps.check_cli.outputs.cli_dir_changed }}
|
||||||
|
docs_dir_changed: ${{ steps.check_docs.outputs.docs_dir_changed }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: "[DEBUG] print variables"
|
||||||
|
run: |
|
||||||
|
echo "env.CLI_DIR: ${{ env.CLI_DIR }}"
|
||||||
|
echo "env.DOCS_BUILD_DIR: ${{ env.DOCS_BUILD_DIR }}"
|
||||||
|
echo "env.DOCS_DIR: ${{ env.DOCS_DIR }}"
|
||||||
|
|
||||||
|
- name: git checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ github.head_ref }}
|
||||||
|
|
||||||
|
- name: set shas
|
||||||
|
id: set_shas
|
||||||
|
run: |
|
||||||
|
THIS_SHA=$(git rev-parse @)
|
||||||
|
LAST_SHA=$(git rev-parse @~1)
|
||||||
|
|
||||||
|
echo "this sha: $THIS_SHA"
|
||||||
|
echo "last sha: $LAST_SHA"
|
||||||
|
|
||||||
|
echo "this_sha=$THIS_SHA" >> $GITHUB_OUTPUT
|
||||||
|
echo "last_sha=$LAST_SHA" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: check for changes in core/dbt/cli
|
||||||
|
id: check_cli
|
||||||
|
run: |
|
||||||
|
CLI_DIR_CHANGES=$(git diff \
|
||||||
|
${{ steps.set_shas.outputs.last_sha }} \
|
||||||
|
${{ steps.set_shas.outputs.this_sha }} \
|
||||||
|
-- ${{ env.CLI_DIR }})
|
||||||
|
|
||||||
|
if [ -n "$CLI_DIR_CHANGES" ]; then
|
||||||
|
echo "changes found"
|
||||||
|
echo $CLI_DIR_CHANGES
|
||||||
|
echo "cli_dir_changed=true" >> $GITHUB_OUTPUT
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "cli_dir_changed=false" >> $GITHUB_OUTPUT
|
||||||
|
echo "no changes found"
|
||||||
|
|
||||||
|
- name: check for changes in core/dbt/docs
|
||||||
|
id: check_docs
|
||||||
|
if: steps.check_cli.outputs.cli_dir_changed == 'false'
|
||||||
|
run: |
|
||||||
|
DOCS_DIR_CHANGES=$(git diff --name-only \
|
||||||
|
${{ steps.set_shas.outputs.last_sha }} \
|
||||||
|
${{ steps.set_shas.outputs.this_sha }} \
|
||||||
|
-- ${{ env.DOCS_DIR }} ':!${{ env.DOCS_BUILD_DIR }}')
|
||||||
|
|
||||||
|
DOCS_BUILD_DIR_CHANGES=$(git diff --name-only \
|
||||||
|
${{ steps.set_shas.outputs.last_sha }} \
|
||||||
|
${{ steps.set_shas.outputs.this_sha }} \
|
||||||
|
-- ${{ env.DOCS_BUILD_DIR }})
|
||||||
|
|
||||||
|
if [ -n "$DOCS_DIR_CHANGES" ] && [ -z "$DOCS_BUILD_DIR_CHANGES" ]; then
|
||||||
|
echo "changes found"
|
||||||
|
echo $DOCS_DIR_CHANGES
|
||||||
|
echo "docs_dir_changed=true" >> $GITHUB_OUTPUT
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "docs_dir_changed=false" >> $GITHUB_OUTPUT
|
||||||
|
echo "no changes found"
|
||||||
|
|
||||||
|
gen_docs:
|
||||||
|
name: generate docs
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [check_gen]
|
||||||
|
if: |
|
||||||
|
needs.check_gen.outputs.cli_dir_changed == 'true'
|
||||||
|
|| needs.check_gen.outputs.docs_dir_changed == 'true'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: "[DEBUG] print variables"
|
||||||
|
run: |
|
||||||
|
echo "env.DOCS_DIR: ${{ env.DOCS_DIR }}"
|
||||||
|
echo "github head_ref: ${{ github.head_ref }}"
|
||||||
|
|
||||||
|
- name: git checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: ${{ github.head_ref }}
|
||||||
|
|
||||||
|
- name: install python
|
||||||
|
uses: actions/setup-python@v4.3.0
|
||||||
|
with:
|
||||||
|
python-version: 3.8
|
||||||
|
|
||||||
|
- name: install dev requirements
|
||||||
|
run: |
|
||||||
|
python3 -m venv env
|
||||||
|
source env/bin/activate
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -r requirements.txt -r dev-requirements.txt
|
||||||
|
|
||||||
|
- name: generate docs
|
||||||
|
run: |
|
||||||
|
source env/bin/activate
|
||||||
|
cd ${{ env.DOCS_DIR }}
|
||||||
|
|
||||||
|
echo "cleaning existing docs"
|
||||||
|
make clean
|
||||||
|
|
||||||
|
echo "creating docs"
|
||||||
|
make html
|
||||||
|
|
||||||
|
- name: debug
|
||||||
|
run: |
|
||||||
|
echo ">>>>> status"
|
||||||
|
git status
|
||||||
|
echo ">>>>> remotes"
|
||||||
|
git remote -v
|
||||||
|
echo ">>>>> branch"
|
||||||
|
git branch -v
|
||||||
|
echo ">>>>> log"
|
||||||
|
git log --pretty=oneline | head -5
|
||||||
|
|
||||||
|
- name: commit docs
|
||||||
|
run: |
|
||||||
|
git config user.name 'Github Build Bot'
|
||||||
|
git config user.email 'buildbot@fishtownanalytics.com'
|
||||||
|
git commit -am "Add generated CLI API docs"
|
||||||
|
git push -u origin ${{ github.head_ref }}
|
||||||
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@@ -73,7 +73,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.7", "3.8", "3.9", "3.10"]
|
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
TOXENV: "unit"
|
TOXENV: "unit"
|
||||||
@@ -118,7 +118,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.7", "3.8", "3.9", "3.10"]
|
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
|
||||||
os: [ubuntu-20.04]
|
os: [ubuntu-20.04]
|
||||||
include:
|
include:
|
||||||
- python-version: 3.8
|
- python-version: 3.8
|
||||||
|
|||||||
11
.github/workflows/stale.yml
vendored
11
.github/workflows/stale.yml
vendored
@@ -9,13 +9,4 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
uses: dbt-labs/actions/.github/workflows/stale-bot-matrix.yml@main
|
||||||
steps:
|
|
||||||
# pinned at v4 (https://github.com/actions/stale/releases/tag/v4.0.0)
|
|
||||||
- uses: actions/stale@cdf15f641adb27a71842045a94023bef6945e3aa
|
|
||||||
with:
|
|
||||||
stale-issue-message: "This issue has been marked as Stale because it has been open for 180 days with no activity. If you would like the issue to remain open, please remove the stale label or comment on the issue, or it will be closed in 7 days."
|
|
||||||
stale-pr-message: "This PR has been marked as Stale because it has been open for 180 days with no activity. If you would like the PR to remain open, please remove the stale label or comment on the PR, or it will be closed in 7 days."
|
|
||||||
close-issue-message: "Although we are closing this issue as stale, it's not gone forever. Issues can be reopened if there is renewed community interest; add a comment to notify the maintainers."
|
|
||||||
# mark issues/PRs stale when they haven't seen activity in 180 days
|
|
||||||
days-before-stale: 180
|
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,6 +11,7 @@ __pycache__/
|
|||||||
env*/
|
env*/
|
||||||
dbt_env/
|
dbt_env/
|
||||||
build/
|
build/
|
||||||
|
!core/dbt/docs/build
|
||||||
develop-eggs/
|
develop-eggs/
|
||||||
dist/
|
dist/
|
||||||
downloads/
|
downloads/
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# Eventually the hooks described here will be run as tests before merging each PR.
|
# Eventually the hooks described here will be run as tests before merging each PR.
|
||||||
|
|
||||||
# TODO: remove global exclusion of tests when testing overhaul is complete
|
# TODO: remove global exclusion of tests when testing overhaul is complete
|
||||||
exclude: ^test/
|
exclude: ^(test/|core/dbt/docs/build/)
|
||||||
|
|
||||||
# Force all unspecified python hooks to run python 3.8
|
# Force all unspecified python hooks to run python 3.8
|
||||||
default_language_version:
|
default_language_version:
|
||||||
|
|||||||
90
CHANGELOG.md
90
CHANGELOG.md
@@ -5,6 +5,96 @@
|
|||||||
- "Breaking changes" listed under a version may require action from end users or external maintainers when upgrading to that version.
|
- "Breaking changes" listed under a version may require action from end users or external maintainers when upgrading to that version.
|
||||||
- Do not edit this file directly. This file is auto-generated using [changie](https://github.com/miniscruff/changie). For details on how to document a change, see [the contributing guide](https://github.com/dbt-labs/dbt-core/blob/main/CONTRIBUTING.md#adding-changelog-entry)
|
- Do not edit this file directly. This file is auto-generated using [changie](https://github.com/miniscruff/changie). For details on how to document a change, see [the contributing guide](https://github.com/dbt-labs/dbt-core/blob/main/CONTRIBUTING.md#adding-changelog-entry)
|
||||||
|
|
||||||
|
## dbt-core 1.4.0-b1 - December 15, 2022
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Added favor-state flag to optionally favor state nodes even if unselected node exists ([#2968](https://github.com/dbt-labs/dbt-core/issues/2968))
|
||||||
|
- Update structured logging. Convert to using protobuf messages. Ensure events are enriched with node_info. ([#5610](https://github.com/dbt-labs/dbt-core/issues/5610))
|
||||||
|
- Friendlier error messages when packages.yml is malformed ([#5486](https://github.com/dbt-labs/dbt-core/issues/5486))
|
||||||
|
- Migrate dbt-utils current_timestamp macros into core + adapters ([#5521](https://github.com/dbt-labs/dbt-core/issues/5521))
|
||||||
|
- Allow partitions in external tables to be supplied as a list ([#5929](https://github.com/dbt-labs/dbt-core/issues/5929))
|
||||||
|
- extend -f flag shorthand for seed command ([#5990](https://github.com/dbt-labs/dbt-core/issues/5990))
|
||||||
|
- This pulls the profile name from args when constructing a RuntimeConfig in lib.py, enabling the dbt-server to override the value that's in the dbt_project.yml ([#6201](https://github.com/dbt-labs/dbt-core/issues/6201))
|
||||||
|
- Adding tarball install method for packages. Allowing package tarball to be specified via url in the packages.yaml. ([#4205](https://github.com/dbt-labs/dbt-core/issues/4205))
|
||||||
|
- Added an md5 function to the base context ([#6246](https://github.com/dbt-labs/dbt-core/issues/6246))
|
||||||
|
- Exposures support metrics in lineage ([#6057](https://github.com/dbt-labs/dbt-core/issues/6057))
|
||||||
|
- Add support for Python 3.11 ([#6147](https://github.com/dbt-labs/dbt-core/issues/6147))
|
||||||
|
- incremental predicates ([#5680](https://github.com/dbt-labs/dbt-core/issues/5680))
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Account for disabled flags on models in schema files more completely ([#3992](https://github.com/dbt-labs/dbt-core/issues/3992))
|
||||||
|
- Add validation of enabled config for metrics, exposures and sources ([#6030](https://github.com/dbt-labs/dbt-core/issues/6030))
|
||||||
|
- check length of args of python model function before accessing it ([#6041](https://github.com/dbt-labs/dbt-core/issues/6041))
|
||||||
|
- Add functors to ensure event types with str-type attributes are initialized to spec, even when provided non-str type params. ([#5436](https://github.com/dbt-labs/dbt-core/issues/5436))
|
||||||
|
- Allow hooks to fail without halting execution flow ([#5625](https://github.com/dbt-labs/dbt-core/issues/5625))
|
||||||
|
- Clarify Error Message for how many models are allowed in a Python file ([#6245](https://github.com/dbt-labs/dbt-core/issues/6245))
|
||||||
|
- After this, will be possible to use default values for dbt.config.get ([#6309](https://github.com/dbt-labs/dbt-core/issues/6309))
|
||||||
|
- Use full path for writing manifest ([#6055](https://github.com/dbt-labs/dbt-core/issues/6055))
|
||||||
|
- [CT-1284] Change Python model default materialization to table ([#6345](https://github.com/dbt-labs/dbt-core/issues/6345))
|
||||||
|
- Repair a regression which prevented basic logging before the logging subsystem is completely configured. ([#6434](https://github.com/dbt-labs/dbt-core/issues/6434))
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
|
||||||
|
- minor doc correction ([dbt-docs/#5791](https://github.com/dbt-labs/dbt-docs/issues/5791))
|
||||||
|
- Generate API docs for new CLI interface ([dbt-docs/#5528](https://github.com/dbt-labs/dbt-docs/issues/5528))
|
||||||
|
- ([dbt-docs/#5880](https://github.com/dbt-labs/dbt-docs/issues/5880))
|
||||||
|
- Fix rendering of sample code for metrics ([dbt-docs/#323](https://github.com/dbt-labs/dbt-docs/issues/323))
|
||||||
|
- Alphabetize `core/dbt/README.md` ([dbt-docs/#6368](https://github.com/dbt-labs/dbt-docs/issues/6368))
|
||||||
|
|
||||||
|
### Under the Hood
|
||||||
|
|
||||||
|
- Put black config in explicit config ([#5946](https://github.com/dbt-labs/dbt-core/issues/5946))
|
||||||
|
- Added flat_graph attribute the Manifest class's deepcopy() coverage ([#5809](https://github.com/dbt-labs/dbt-core/issues/5809))
|
||||||
|
- Add mypy configs so `mypy` passes from CLI ([#5983](https://github.com/dbt-labs/dbt-core/issues/5983))
|
||||||
|
- Exception message cleanup. ([#6023](https://github.com/dbt-labs/dbt-core/issues/6023))
|
||||||
|
- Add dmypy cache to gitignore ([#6028](https://github.com/dbt-labs/dbt-core/issues/6028))
|
||||||
|
- Provide useful errors when the value of 'materialized' is invalid ([#5229](https://github.com/dbt-labs/dbt-core/issues/5229))
|
||||||
|
- Clean up string formatting ([#6068](https://github.com/dbt-labs/dbt-core/issues/6068))
|
||||||
|
- Fixed extra whitespace in strings introduced by black. ([#1350](https://github.com/dbt-labs/dbt-core/issues/1350))
|
||||||
|
- Remove the 'root_path' field from most nodes ([#6171](https://github.com/dbt-labs/dbt-core/issues/6171))
|
||||||
|
- Combine certain logging events with different levels ([#6173](https://github.com/dbt-labs/dbt-core/issues/6173))
|
||||||
|
- Convert threading tests to pytest ([#5942](https://github.com/dbt-labs/dbt-core/issues/5942))
|
||||||
|
- Convert postgres index tests to pytest ([#5770](https://github.com/dbt-labs/dbt-core/issues/5770))
|
||||||
|
- Convert use color tests to pytest ([#5771](https://github.com/dbt-labs/dbt-core/issues/5771))
|
||||||
|
- Add github actions workflow to generate high level CLI API docs ([#5942](https://github.com/dbt-labs/dbt-core/issues/5942))
|
||||||
|
- Functionality-neutral refactor of event logging system to improve encapsulation and modularity. ([#6139](https://github.com/dbt-labs/dbt-core/issues/6139))
|
||||||
|
- Consolidate ParsedNode and CompiledNode classes ([#6383](https://github.com/dbt-labs/dbt-core/issues/6383))
|
||||||
|
- Prevent doc gen workflow from running on forks ([#6386](https://github.com/dbt-labs/dbt-core/issues/6386))
|
||||||
|
- Fix intermittent database connection failure in Windows CI test ([#6394](https://github.com/dbt-labs/dbt-core/issues/6394))
|
||||||
|
- Refactor and clean up manifest nodes ([#6426](https://github.com/dbt-labs/dbt-core/issues/6426))
|
||||||
|
- Restore important legacy logging behaviors, following refactor which removed them ([#6437](https://github.com/dbt-labs/dbt-core/issues/6437))
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Update pathspec requirement from ~=0.9.0 to >=0.9,<0.11 in /core ([#5917](https://github.com/dbt-labs/dbt-core/pull/5917))
|
||||||
|
- Bump black from 22.8.0 to 22.10.0 ([#6019](https://github.com/dbt-labs/dbt-core/pull/6019))
|
||||||
|
- Bump mashumaro[msgpack] from 3.0.4 to 3.1.1 in /core ([#6108](https://github.com/dbt-labs/dbt-core/pull/6108))
|
||||||
|
- Update colorama requirement from <0.4.6,>=0.3.9 to >=0.3.9,<0.4.7 in /core ([#6144](https://github.com/dbt-labs/dbt-core/pull/6144))
|
||||||
|
- Bump mashumaro[msgpack] from 3.1.1 to 3.2 in /core ([#4904](https://github.com/dbt-labs/dbt-core/issues/4904))
|
||||||
|
|
||||||
|
### Contributors
|
||||||
|
- [@andy-clapson](https://github.com/andy-clapson) ([dbt-docs/#5791](https://github.com/dbt-labs/dbt-docs/issues/5791))
|
||||||
|
- [@chamini2](https://github.com/chamini2) ([#6041](https://github.com/dbt-labs/dbt-core/issues/6041))
|
||||||
|
- [@daniel-murray](https://github.com/daniel-murray) ([#2968](https://github.com/dbt-labs/dbt-core/issues/2968))
|
||||||
|
- [@dave-connors-3](https://github.com/dave-connors-3) ([#5990](https://github.com/dbt-labs/dbt-core/issues/5990))
|
||||||
|
- [@dbeatty10](https://github.com/dbeatty10) ([dbt-docs/#6368](https://github.com/dbt-labs/dbt-docs/issues/6368), [#6394](https://github.com/dbt-labs/dbt-core/issues/6394))
|
||||||
|
- [@devmessias](https://github.com/devmessias) ([#6309](https://github.com/dbt-labs/dbt-core/issues/6309))
|
||||||
|
- [@eve-johns](https://github.com/eve-johns) ([#6068](https://github.com/dbt-labs/dbt-core/issues/6068))
|
||||||
|
- [@haritamar](https://github.com/haritamar) ([#6246](https://github.com/dbt-labs/dbt-core/issues/6246))
|
||||||
|
- [@jared-rimmer](https://github.com/jared-rimmer) ([#5486](https://github.com/dbt-labs/dbt-core/issues/5486))
|
||||||
|
- [@josephberni](https://github.com/josephberni) ([#2968](https://github.com/dbt-labs/dbt-core/issues/2968))
|
||||||
|
- [@joshuataylor](https://github.com/joshuataylor) ([#6147](https://github.com/dbt-labs/dbt-core/issues/6147))
|
||||||
|
- [@justbldwn](https://github.com/justbldwn) ([#6245](https://github.com/dbt-labs/dbt-core/issues/6245))
|
||||||
|
- [@luke-bassett](https://github.com/luke-bassett) ([#1350](https://github.com/dbt-labs/dbt-core/issues/1350))
|
||||||
|
- [@max-sixty](https://github.com/max-sixty) ([#5946](https://github.com/dbt-labs/dbt-core/issues/5946), [#5983](https://github.com/dbt-labs/dbt-core/issues/5983), [#6028](https://github.com/dbt-labs/dbt-core/issues/6028))
|
||||||
|
- [@paulbenschmidt](https://github.com/paulbenschmidt) ([dbt-docs/#5880](https://github.com/dbt-labs/dbt-docs/issues/5880))
|
||||||
|
- [@pgoslatara](https://github.com/pgoslatara) ([#5929](https://github.com/dbt-labs/dbt-core/issues/5929))
|
||||||
|
- [@racheldaniel](https://github.com/racheldaniel) ([#6201](https://github.com/dbt-labs/dbt-core/issues/6201))
|
||||||
|
- [@timle2](https://github.com/timle2) ([#4205](https://github.com/dbt-labs/dbt-core/issues/4205))
|
||||||
|
- [@dave-connors-3](https://github.com/dave-connors-3) ([#5680](https://github.com/dbt-labs/dbt-core/issues/5680))
|
||||||
|
|
||||||
|
|
||||||
## Previous Releases
|
## Previous Releases
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ There are some tools that will be helpful to you in developing locally. While th
|
|||||||
|
|
||||||
These are the tools used in `dbt-core` development and testing:
|
These are the tools used in `dbt-core` development and testing:
|
||||||
|
|
||||||
- [`tox`](https://tox.readthedocs.io/en/latest/) to manage virtualenvs across python versions. We currently target the latest patch releases for Python 3.7, 3.8, 3.9, and 3.10
|
- [`tox`](https://tox.readthedocs.io/en/latest/) to manage virtualenvs across python versions. We currently target the latest patch releases for Python 3.7, 3.8, 3.9, 3.10 and 3.11
|
||||||
- [`pytest`](https://docs.pytest.org/en/latest/) to define, discover, and run tests
|
- [`pytest`](https://docs.pytest.org/en/latest/) to define, discover, and run tests
|
||||||
- [`flake8`](https://flake8.pycqa.org/en/latest/) for code linting
|
- [`flake8`](https://flake8.pycqa.org/en/latest/) for code linting
|
||||||
- [`black`](https://github.com/psf/black) for code formatting
|
- [`black`](https://github.com/psf/black) for code formatting
|
||||||
@@ -160,7 +160,7 @@ suites.
|
|||||||
|
|
||||||
#### `tox`
|
#### `tox`
|
||||||
|
|
||||||
[`tox`](https://tox.readthedocs.io/en/latest/) takes care of managing virtualenvs and install dependencies in order to run tests. You can also run tests in parallel, for example, you can run unit tests for Python 3.7, Python 3.8, Python 3.9, and Python 3.10 checks in parallel with `tox -p`. Also, you can run unit tests for specific python versions with `tox -e py37`. The configuration for these tests in located in `tox.ini`.
|
[`tox`](https://tox.readthedocs.io/en/latest/) takes care of managing virtualenvs and install dependencies in order to run tests. You can also run tests in parallel, for example, you can run unit tests for Python 3.7, Python 3.8, Python 3.9, Python 3.10 and Python 3.11 checks in parallel with `tox -p`. Also, you can run unit tests for specific python versions with `tox -e py37`. The configuration for these tests in located in `tox.ini`.
|
||||||
|
|
||||||
#### `pytest`
|
#### `pytest`
|
||||||
|
|
||||||
@@ -201,13 +201,21 @@ Here are some general rules for adding tests:
|
|||||||
* Sometimes flake8 complains about lines that are actually fine, in which case you can put a comment on the line such as: # noqa or # noqa: ANNN, where ANNN is the error code that flake8 issues.
|
* Sometimes flake8 complains about lines that are actually fine, in which case you can put a comment on the line such as: # noqa or # noqa: ANNN, where ANNN is the error code that flake8 issues.
|
||||||
* To collect output for `CProfile`, run dbt with the `-r` option and the name of an output file, i.e. `dbt -r dbt.cprof run`. If you just want to profile parsing, you can do: `dbt -r dbt.cprof parse`. `pip` install `snakeviz` to view the output. Run `snakeviz dbt.cprof` and output will be rendered in a browser window.
|
* To collect output for `CProfile`, run dbt with the `-r` option and the name of an output file, i.e. `dbt -r dbt.cprof run`. If you just want to profile parsing, you can do: `dbt -r dbt.cprof parse`. `pip` install `snakeviz` to view the output. Run `snakeviz dbt.cprof` and output will be rendered in a browser window.
|
||||||
|
|
||||||
## Adding a CHANGELOG Entry
|
## Adding or modifying a CHANGELOG Entry
|
||||||
|
|
||||||
We use [changie](https://changie.dev) to generate `CHANGELOG` entries. **Note:** Do not edit the `CHANGELOG.md` directly. Your modifications will be lost.
|
We use [changie](https://changie.dev) to generate `CHANGELOG` entries. **Note:** Do not edit the `CHANGELOG.md` directly. Your modifications will be lost.
|
||||||
|
|
||||||
Follow the steps to [install `changie`](https://changie.dev/guide/installation/) for your system.
|
Follow the steps to [install `changie`](https://changie.dev/guide/installation/) for your system.
|
||||||
|
|
||||||
Once changie is installed and your PR is created, simply run `changie new` and changie will walk you through the process of creating a changelog entry. Commit the file that's created and your changelog entry is complete!
|
Once changie is installed and your PR is created for a new feature, simply run the following command and changie will walk you through the process of creating a changelog entry:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
changie new
|
||||||
|
```
|
||||||
|
|
||||||
|
Commit the file that's created and your changelog entry is complete!
|
||||||
|
|
||||||
|
If you are contributing to a feature already in progress, you will modify the changie yaml file in dbt/.changes/unreleased/ related to your change. If you need help finding this file, please ask within the discussion for the pull request!
|
||||||
|
|
||||||
You don't need to worry about which `dbt-core` version your change will go into. Just create the changelog entry with `changie`, and open your PR against the `main` branch. All merged changes will be included in the next minor version of `dbt-core`. The Core maintainers _may_ choose to "backport" specific changes in order to patch older minor versions. In that case, a maintainer will take care of that backport after merging your PR, before releasing the new version of `dbt-core`.
|
You don't need to worry about which `dbt-core` version your change will go into. Just create the changelog entry with `changie`, and open your PR against the `main` branch. All merged changes will be included in the next minor version of `dbt-core`. The Core maintainers _may_ choose to "backport" specific changes in order to patch older minor versions. In that case, a maintainer will take care of that backport after merging your PR, before releasing the new version of `dbt-core`.
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ RUN apt-get update \
|
|||||||
python3.10 \
|
python3.10 \
|
||||||
python3.10-dev \
|
python3.10-dev \
|
||||||
python3.10-venv \
|
python3.10-venv \
|
||||||
|
python3.11 \
|
||||||
|
python3.11-dev \
|
||||||
|
python3.11-venv \
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
|
|||||||
@@ -2,50 +2,59 @@
|
|||||||
|
|
||||||
## The following are individual files in this directory.
|
## The following are individual files in this directory.
|
||||||
|
|
||||||
### deprecations.py
|
|
||||||
|
|
||||||
### flags.py
|
|
||||||
|
|
||||||
### main.py
|
|
||||||
|
|
||||||
### tracking.py
|
|
||||||
|
|
||||||
### version.py
|
|
||||||
|
|
||||||
### lib.py
|
|
||||||
|
|
||||||
### node_types.py
|
|
||||||
|
|
||||||
### helper_types.py
|
|
||||||
|
|
||||||
### links.py
|
|
||||||
|
|
||||||
### semver.py
|
|
||||||
|
|
||||||
### ui.py
|
|
||||||
|
|
||||||
### compilation.py
|
### compilation.py
|
||||||
|
|
||||||
|
### constants.py
|
||||||
|
|
||||||
### dataclass_schema.py
|
### dataclass_schema.py
|
||||||
|
|
||||||
|
### deprecations.py
|
||||||
|
|
||||||
### exceptions.py
|
### exceptions.py
|
||||||
|
|
||||||
|
### flags.py
|
||||||
|
|
||||||
|
### helper_types.py
|
||||||
|
|
||||||
### hooks.py
|
### hooks.py
|
||||||
|
|
||||||
|
### lib.py
|
||||||
|
|
||||||
|
### links.py
|
||||||
|
|
||||||
### logger.py
|
### logger.py
|
||||||
|
|
||||||
|
### main.py
|
||||||
|
|
||||||
|
### node_types.py
|
||||||
|
|
||||||
### profiler.py
|
### profiler.py
|
||||||
|
|
||||||
|
### selected_resources.py
|
||||||
|
|
||||||
|
### semver.py
|
||||||
|
|
||||||
|
### tracking.py
|
||||||
|
|
||||||
|
### ui.py
|
||||||
|
|
||||||
### utils.py
|
### utils.py
|
||||||
|
|
||||||
|
### version.py
|
||||||
|
|
||||||
|
|
||||||
## The subdirectories will be documented in a README in the subdirectory
|
## The subdirectories will be documented in a README in the subdirectory
|
||||||
* config
|
|
||||||
* include
|
|
||||||
* adapters
|
* adapters
|
||||||
* context
|
* cli
|
||||||
* deps
|
|
||||||
* graph
|
|
||||||
* task
|
|
||||||
* clients
|
* clients
|
||||||
|
* config
|
||||||
|
* context
|
||||||
|
* contracts
|
||||||
|
* deps
|
||||||
|
* docs
|
||||||
* events
|
* events
|
||||||
|
* graph
|
||||||
|
* include
|
||||||
|
* parser
|
||||||
|
* task
|
||||||
|
* tests
|
||||||
|
|||||||
@@ -41,13 +41,14 @@ from dbt.events.functions import fire_event
|
|||||||
from dbt.events.types import (
|
from dbt.events.types import (
|
||||||
NewConnection,
|
NewConnection,
|
||||||
ConnectionReused,
|
ConnectionReused,
|
||||||
|
ConnectionLeftOpenInCleanup,
|
||||||
ConnectionLeftOpen,
|
ConnectionLeftOpen,
|
||||||
ConnectionLeftOpen2,
|
ConnectionClosedInCleanup,
|
||||||
ConnectionClosed,
|
ConnectionClosed,
|
||||||
ConnectionClosed2,
|
|
||||||
Rollback,
|
Rollback,
|
||||||
RollbackFailed,
|
RollbackFailed,
|
||||||
)
|
)
|
||||||
|
from dbt.events.contextvars import get_node_info
|
||||||
from dbt import flags
|
from dbt import flags
|
||||||
from dbt.utils import cast_to_str
|
from dbt.utils import cast_to_str
|
||||||
|
|
||||||
@@ -169,7 +170,9 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
|
|||||||
if conn.name == conn_name and conn.state == "open":
|
if conn.name == conn_name and conn.state == "open":
|
||||||
return conn
|
return conn
|
||||||
|
|
||||||
fire_event(NewConnection(conn_name=conn_name, conn_type=self.TYPE))
|
fire_event(
|
||||||
|
NewConnection(conn_name=conn_name, conn_type=self.TYPE, node_info=get_node_info())
|
||||||
|
)
|
||||||
|
|
||||||
if conn.state == "open":
|
if conn.state == "open":
|
||||||
fire_event(ConnectionReused(conn_name=conn_name))
|
fire_event(ConnectionReused(conn_name=conn_name))
|
||||||
@@ -306,9 +309,9 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
|
|||||||
with self.lock:
|
with self.lock:
|
||||||
for connection in self.thread_connections.values():
|
for connection in self.thread_connections.values():
|
||||||
if connection.state not in {"closed", "init"}:
|
if connection.state not in {"closed", "init"}:
|
||||||
fire_event(ConnectionLeftOpen(conn_name=cast_to_str(connection.name)))
|
fire_event(ConnectionLeftOpenInCleanup(conn_name=cast_to_str(connection.name)))
|
||||||
else:
|
else:
|
||||||
fire_event(ConnectionClosed(conn_name=cast_to_str(connection.name)))
|
fire_event(ConnectionClosedInCleanup(conn_name=cast_to_str(connection.name)))
|
||||||
self.close(connection)
|
self.close(connection)
|
||||||
|
|
||||||
# garbage collect these connections
|
# garbage collect these connections
|
||||||
@@ -336,7 +339,9 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
|
|||||||
except Exception:
|
except Exception:
|
||||||
fire_event(
|
fire_event(
|
||||||
RollbackFailed(
|
RollbackFailed(
|
||||||
conn_name=cast_to_str(connection.name), exc_info=traceback.format_exc()
|
conn_name=cast_to_str(connection.name),
|
||||||
|
exc_info=traceback.format_exc(),
|
||||||
|
node_info=get_node_info(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -345,10 +350,16 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
|
|||||||
"""Perform the actual close operation."""
|
"""Perform the actual close operation."""
|
||||||
# On windows, sometimes connection handles don't have a close() attr.
|
# On windows, sometimes connection handles don't have a close() attr.
|
||||||
if hasattr(connection.handle, "close"):
|
if hasattr(connection.handle, "close"):
|
||||||
fire_event(ConnectionClosed2(conn_name=cast_to_str(connection.name)))
|
fire_event(
|
||||||
|
ConnectionClosed(conn_name=cast_to_str(connection.name), node_info=get_node_info())
|
||||||
|
)
|
||||||
connection.handle.close()
|
connection.handle.close()
|
||||||
else:
|
else:
|
||||||
fire_event(ConnectionLeftOpen2(conn_name=cast_to_str(connection.name)))
|
fire_event(
|
||||||
|
ConnectionLeftOpen(
|
||||||
|
conn_name=cast_to_str(connection.name), node_info=get_node_info()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _rollback(cls, connection: Connection) -> None:
|
def _rollback(cls, connection: Connection) -> None:
|
||||||
@@ -359,7 +370,7 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
|
|||||||
f'"{connection.name}", but it does not have one open!'
|
f'"{connection.name}", but it does not have one open!'
|
||||||
)
|
)
|
||||||
|
|
||||||
fire_event(Rollback(conn_name=cast_to_str(connection.name)))
|
fire_event(Rollback(conn_name=cast_to_str(connection.name), node_info=get_node_info()))
|
||||||
cls._rollback_handle(connection)
|
cls._rollback_handle(connection)
|
||||||
|
|
||||||
connection.transaction_open = False
|
connection.transaction_open = False
|
||||||
@@ -371,7 +382,7 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
|
|||||||
return connection
|
return connection
|
||||||
|
|
||||||
if connection.transaction_open and connection.handle:
|
if connection.transaction_open and connection.handle:
|
||||||
fire_event(Rollback(conn_name=cast_to_str(connection.name)))
|
fire_event(Rollback(conn_name=cast_to_str(connection.name), node_info=get_node_info()))
|
||||||
cls._rollback_handle(connection)
|
cls._rollback_handle(connection)
|
||||||
connection.transaction_open = False
|
connection.transaction_open = False
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ from typing import (
|
|||||||
List,
|
List,
|
||||||
Mapping,
|
Mapping,
|
||||||
Iterator,
|
Iterator,
|
||||||
Union,
|
|
||||||
Set,
|
Set,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,13 +22,20 @@ import agate
|
|||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
from dbt.exceptions import (
|
from dbt.exceptions import (
|
||||||
raise_database_error,
|
|
||||||
raise_compiler_error,
|
|
||||||
invalid_type_error,
|
|
||||||
get_relation_returned_multiple_results,
|
|
||||||
InternalException,
|
InternalException,
|
||||||
|
InvalidMacroArgType,
|
||||||
|
InvalidMacroResult,
|
||||||
|
InvalidQuoteConfigType,
|
||||||
NotImplementedException,
|
NotImplementedException,
|
||||||
|
NullRelationCacheAttempted,
|
||||||
|
NullRelationDropAttempted,
|
||||||
|
RelationReturnedMultipleResults,
|
||||||
|
RenameToNoneAttempted,
|
||||||
RuntimeException,
|
RuntimeException,
|
||||||
|
SnapshotTargetIncomplete,
|
||||||
|
SnapshotTargetNotSnapshotTable,
|
||||||
|
UnexpectedNull,
|
||||||
|
UnexpectedNonTimestamp,
|
||||||
)
|
)
|
||||||
|
|
||||||
from dbt.adapters.protocol import (
|
from dbt.adapters.protocol import (
|
||||||
@@ -38,16 +44,15 @@ from dbt.adapters.protocol import (
|
|||||||
)
|
)
|
||||||
from dbt.clients.agate_helper import empty_table, merge_tables, table_from_rows
|
from dbt.clients.agate_helper import empty_table, merge_tables, table_from_rows
|
||||||
from dbt.clients.jinja import MacroGenerator
|
from dbt.clients.jinja import MacroGenerator
|
||||||
from dbt.contracts.graph.compiled import CompileResultNode, CompiledSeedNode
|
|
||||||
from dbt.contracts.graph.manifest import Manifest, MacroManifest
|
from dbt.contracts.graph.manifest import Manifest, MacroManifest
|
||||||
from dbt.contracts.graph.parsed import ParsedSeedNode
|
from dbt.contracts.graph.nodes import ResultNode
|
||||||
from dbt.exceptions import warn_or_error
|
from dbt.events.functions import fire_event, warn_or_error
|
||||||
from dbt.events.functions import fire_event
|
|
||||||
from dbt.events.types import (
|
from dbt.events.types import (
|
||||||
CacheMiss,
|
CacheMiss,
|
||||||
ListRelations,
|
ListRelations,
|
||||||
CodeExecution,
|
CodeExecution,
|
||||||
CodeExecutionStatus,
|
CodeExecutionStatus,
|
||||||
|
CatalogGenerationError,
|
||||||
)
|
)
|
||||||
from dbt.utils import filter_null_values, executor, cast_to_str
|
from dbt.utils import filter_null_values, executor, cast_to_str
|
||||||
|
|
||||||
@@ -64,9 +69,6 @@ from dbt.adapters.base import Credentials
|
|||||||
from dbt.adapters.cache import RelationsCache, _make_ref_key_msg
|
from dbt.adapters.cache import RelationsCache, _make_ref_key_msg
|
||||||
|
|
||||||
|
|
||||||
SeedModel = Union[ParsedSeedNode, CompiledSeedNode]
|
|
||||||
|
|
||||||
|
|
||||||
GET_CATALOG_MACRO_NAME = "get_catalog"
|
GET_CATALOG_MACRO_NAME = "get_catalog"
|
||||||
FRESHNESS_MACRO_NAME = "collect_freshness"
|
FRESHNESS_MACRO_NAME = "collect_freshness"
|
||||||
|
|
||||||
@@ -102,18 +104,10 @@ def _utc(dt: Optional[datetime], source: BaseRelation, field_name: str) -> datet
|
|||||||
assume the datetime is already for UTC and add the timezone.
|
assume the datetime is already for UTC and add the timezone.
|
||||||
"""
|
"""
|
||||||
if dt is None:
|
if dt is None:
|
||||||
raise raise_database_error(
|
raise UnexpectedNull(field_name, source)
|
||||||
"Expected a non-null value when querying field '{}' of table "
|
|
||||||
" {} but received value 'null' instead".format(field_name, source)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif not hasattr(dt, "tzinfo"):
|
elif not hasattr(dt, "tzinfo"):
|
||||||
raise raise_database_error(
|
raise UnexpectedNonTimestamp(field_name, source, dt)
|
||||||
"Expected a timestamp value when querying field '{}' of table "
|
|
||||||
"{} but received value of type '{}' instead".format(
|
|
||||||
field_name, source, type(dt).__name__
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif dt.tzinfo:
|
elif dt.tzinfo:
|
||||||
return dt.astimezone(pytz.UTC)
|
return dt.astimezone(pytz.UTC)
|
||||||
@@ -243,9 +237,7 @@ class BaseAdapter(metaclass=AdapterMeta):
|
|||||||
return conn.name
|
return conn.name
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def connection_named(
|
def connection_named(self, name: str, node: Optional[ResultNode] = None) -> Iterator[None]:
|
||||||
self, name: str, node: Optional[CompileResultNode] = None
|
|
||||||
) -> Iterator[None]:
|
|
||||||
try:
|
try:
|
||||||
if self.connections.query_header is not None:
|
if self.connections.query_header is not None:
|
||||||
self.connections.query_header.set(name, node)
|
self.connections.query_header.set(name, node)
|
||||||
@@ -257,7 +249,7 @@ class BaseAdapter(metaclass=AdapterMeta):
|
|||||||
self.connections.query_header.reset()
|
self.connections.query_header.reset()
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def connection_for(self, node: CompileResultNode) -> Iterator[None]:
|
def connection_for(self, node: ResultNode) -> Iterator[None]:
|
||||||
with self.connection_named(node.unique_id, node):
|
with self.connection_named(node.unique_id, node):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
@@ -372,7 +364,7 @@ class BaseAdapter(metaclass=AdapterMeta):
|
|||||||
lowercase strings.
|
lowercase strings.
|
||||||
"""
|
"""
|
||||||
info_schema_name_map = SchemaSearchMap()
|
info_schema_name_map = SchemaSearchMap()
|
||||||
nodes: Iterator[CompileResultNode] = chain(
|
nodes: Iterator[ResultNode] = chain(
|
||||||
[
|
[
|
||||||
node
|
node
|
||||||
for node in manifest.nodes.values()
|
for node in manifest.nodes.values()
|
||||||
@@ -441,7 +433,7 @@ class BaseAdapter(metaclass=AdapterMeta):
|
|||||||
"""Cache a new relation in dbt. It will show up in `list relations`."""
|
"""Cache a new relation in dbt. It will show up in `list relations`."""
|
||||||
if relation is None:
|
if relation is None:
|
||||||
name = self.nice_connection_name()
|
name = self.nice_connection_name()
|
||||||
raise_compiler_error("Attempted to cache a null relation for {}".format(name))
|
raise NullRelationCacheAttempted(name)
|
||||||
self.cache.add(relation)
|
self.cache.add(relation)
|
||||||
# so jinja doesn't render things
|
# so jinja doesn't render things
|
||||||
return ""
|
return ""
|
||||||
@@ -453,7 +445,7 @@ class BaseAdapter(metaclass=AdapterMeta):
|
|||||||
"""
|
"""
|
||||||
if relation is None:
|
if relation is None:
|
||||||
name = self.nice_connection_name()
|
name = self.nice_connection_name()
|
||||||
raise_compiler_error("Attempted to drop a null relation for {}".format(name))
|
raise NullRelationDropAttempted(name)
|
||||||
self.cache.drop(relation)
|
self.cache.drop(relation)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@@ -470,9 +462,7 @@ class BaseAdapter(metaclass=AdapterMeta):
|
|||||||
name = self.nice_connection_name()
|
name = self.nice_connection_name()
|
||||||
src_name = _relation_name(from_relation)
|
src_name = _relation_name(from_relation)
|
||||||
dst_name = _relation_name(to_relation)
|
dst_name = _relation_name(to_relation)
|
||||||
raise_compiler_error(
|
raise RenameToNoneAttempted(src_name, dst_name, name)
|
||||||
"Attempted to rename {} to {} for {}".format(src_name, dst_name, name)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.cache.rename(from_relation, to_relation)
|
self.cache.rename(from_relation, to_relation)
|
||||||
return ""
|
return ""
|
||||||
@@ -622,7 +612,7 @@ class BaseAdapter(metaclass=AdapterMeta):
|
|||||||
to_relation.
|
to_relation.
|
||||||
"""
|
"""
|
||||||
if not isinstance(from_relation, self.Relation):
|
if not isinstance(from_relation, self.Relation):
|
||||||
invalid_type_error(
|
raise InvalidMacroArgType(
|
||||||
method_name="get_missing_columns",
|
method_name="get_missing_columns",
|
||||||
arg_name="from_relation",
|
arg_name="from_relation",
|
||||||
got_value=from_relation,
|
got_value=from_relation,
|
||||||
@@ -630,7 +620,7 @@ class BaseAdapter(metaclass=AdapterMeta):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not isinstance(to_relation, self.Relation):
|
if not isinstance(to_relation, self.Relation):
|
||||||
invalid_type_error(
|
raise InvalidMacroArgType(
|
||||||
method_name="get_missing_columns",
|
method_name="get_missing_columns",
|
||||||
arg_name="to_relation",
|
arg_name="to_relation",
|
||||||
got_value=to_relation,
|
got_value=to_relation,
|
||||||
@@ -655,7 +645,7 @@ class BaseAdapter(metaclass=AdapterMeta):
|
|||||||
incorrect.
|
incorrect.
|
||||||
"""
|
"""
|
||||||
if not isinstance(relation, self.Relation):
|
if not isinstance(relation, self.Relation):
|
||||||
invalid_type_error(
|
raise InvalidMacroArgType(
|
||||||
method_name="valid_snapshot_target",
|
method_name="valid_snapshot_target",
|
||||||
arg_name="relation",
|
arg_name="relation",
|
||||||
got_value=relation,
|
got_value=relation,
|
||||||
@@ -676,24 +666,16 @@ class BaseAdapter(metaclass=AdapterMeta):
|
|||||||
|
|
||||||
if missing:
|
if missing:
|
||||||
if extra:
|
if extra:
|
||||||
msg = (
|
raise SnapshotTargetIncomplete(extra, missing)
|
||||||
'Snapshot target has ("{}") but not ("{}") - is it an '
|
|
||||||
"unmigrated previous version archive?".format(
|
|
||||||
'", "'.join(extra), '", "'.join(missing)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
msg = 'Snapshot target is not a snapshot table (missing "{}")'.format(
|
raise SnapshotTargetNotSnapshotTable(missing)
|
||||||
'", "'.join(missing)
|
|
||||||
)
|
|
||||||
raise_compiler_error(msg)
|
|
||||||
|
|
||||||
@available.parse_none
|
@available.parse_none
|
||||||
def expand_target_column_types(
|
def expand_target_column_types(
|
||||||
self, from_relation: BaseRelation, to_relation: BaseRelation
|
self, from_relation: BaseRelation, to_relation: BaseRelation
|
||||||
) -> None:
|
) -> None:
|
||||||
if not isinstance(from_relation, self.Relation):
|
if not isinstance(from_relation, self.Relation):
|
||||||
invalid_type_error(
|
raise InvalidMacroArgType(
|
||||||
method_name="expand_target_column_types",
|
method_name="expand_target_column_types",
|
||||||
arg_name="from_relation",
|
arg_name="from_relation",
|
||||||
got_value=from_relation,
|
got_value=from_relation,
|
||||||
@@ -701,7 +683,7 @@ class BaseAdapter(metaclass=AdapterMeta):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not isinstance(to_relation, self.Relation):
|
if not isinstance(to_relation, self.Relation):
|
||||||
invalid_type_error(
|
raise InvalidMacroArgType(
|
||||||
method_name="expand_target_column_types",
|
method_name="expand_target_column_types",
|
||||||
arg_name="to_relation",
|
arg_name="to_relation",
|
||||||
got_value=to_relation,
|
got_value=to_relation,
|
||||||
@@ -783,7 +765,7 @@ class BaseAdapter(metaclass=AdapterMeta):
|
|||||||
"schema": schema,
|
"schema": schema,
|
||||||
"database": database,
|
"database": database,
|
||||||
}
|
}
|
||||||
get_relation_returned_multiple_results(kwargs, matches)
|
raise RelationReturnedMultipleResults(kwargs, matches)
|
||||||
|
|
||||||
elif matches:
|
elif matches:
|
||||||
return matches[0]
|
return matches[0]
|
||||||
@@ -847,10 +829,7 @@ class BaseAdapter(metaclass=AdapterMeta):
|
|||||||
elif quote_config is None:
|
elif quote_config is None:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise_compiler_error(
|
raise InvalidQuoteConfigType(quote_config)
|
||||||
f'The seed configuration value of "quote_columns" has an '
|
|
||||||
f"invalid type {type(quote_config)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if quote_columns:
|
if quote_columns:
|
||||||
return self.quote(column)
|
return self.quote(column)
|
||||||
@@ -1100,11 +1079,7 @@ class BaseAdapter(metaclass=AdapterMeta):
|
|||||||
# now we have a 1-row table of the maximum `loaded_at_field` value and
|
# now we have a 1-row table of the maximum `loaded_at_field` value and
|
||||||
# the current time according to the db.
|
# the current time according to the db.
|
||||||
if len(table) != 1 or len(table[0]) != 2:
|
if len(table) != 1 or len(table[0]) != 2:
|
||||||
raise_compiler_error(
|
raise InvalidMacroResult(FRESHNESS_MACRO_NAME, table)
|
||||||
'Got an invalid result from "{}" macro: {}'.format(
|
|
||||||
FRESHNESS_MACRO_NAME, [tuple(r) for r in table]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if table[0][0] is None:
|
if table[0][0] is None:
|
||||||
# no records in the table, so really the max_loaded_at was
|
# no records in the table, so really the max_loaded_at was
|
||||||
# infinitely long ago. Just call it 0:00 January 1 year UTC
|
# infinitely long ago. Just call it 0:00 January 1 year UTC
|
||||||
@@ -1327,7 +1302,7 @@ def catch_as_completed(
|
|||||||
elif isinstance(exc, KeyboardInterrupt) or not isinstance(exc, Exception):
|
elif isinstance(exc, KeyboardInterrupt) or not isinstance(exc, Exception):
|
||||||
raise exc
|
raise exc
|
||||||
else:
|
else:
|
||||||
warn_or_error(f"Encountered an error while generating catalog: {str(exc)}")
|
warn_or_error(CatalogGenerationError(exc=str(exc)))
|
||||||
# exc is not None, derives from Exception, and isn't ctrl+c
|
# exc is not None, derives from Exception, and isn't ctrl+c
|
||||||
exceptions.append(exc)
|
exceptions.append(exc)
|
||||||
return merge_tables(tables), exceptions
|
return merge_tables(tables), exceptions
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from dbt.clients.jinja import QueryStringGenerator
|
|||||||
|
|
||||||
from dbt.context.manifest import generate_query_header_context
|
from dbt.context.manifest import generate_query_header_context
|
||||||
from dbt.contracts.connection import AdapterRequiredConfig, QueryComment
|
from dbt.contracts.connection import AdapterRequiredConfig, QueryComment
|
||||||
from dbt.contracts.graph.compiled import CompileResultNode
|
from dbt.contracts.graph.nodes import ResultNode
|
||||||
from dbt.contracts.graph.manifest import Manifest
|
from dbt.contracts.graph.manifest import Manifest
|
||||||
from dbt.exceptions import RuntimeException
|
from dbt.exceptions import RuntimeException
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ class MacroQueryStringSetter:
|
|||||||
def reset(self):
|
def reset(self):
|
||||||
self.set("master", None)
|
self.set("master", None)
|
||||||
|
|
||||||
def set(self, name: str, node: Optional[CompileResultNode]):
|
def set(self, name: str, node: Optional[ResultNode]):
|
||||||
wrapped: Optional[NodeWrapper] = None
|
wrapped: Optional[NodeWrapper] = None
|
||||||
if node is not None:
|
if node is not None:
|
||||||
wrapped = NodeWrapper(node)
|
wrapped = NodeWrapper(node)
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
from collections.abc import Hashable
|
from collections.abc import Hashable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass, field
|
||||||
from typing import Optional, TypeVar, Any, Type, Dict, Union, Iterator, Tuple, Set
|
from typing import Optional, TypeVar, Any, Type, Dict, Iterator, Tuple, Set
|
||||||
|
|
||||||
from dbt.contracts.graph.compiled import CompiledNode
|
from dbt.contracts.graph.nodes import SourceDefinition, ManifestNode, ResultNode, ParsedNode
|
||||||
from dbt.contracts.graph.parsed import ParsedSourceDefinition, ParsedNode
|
|
||||||
from dbt.contracts.relation import (
|
from dbt.contracts.relation import (
|
||||||
RelationType,
|
RelationType,
|
||||||
ComponentName,
|
ComponentName,
|
||||||
@@ -12,7 +11,7 @@ from dbt.contracts.relation import (
|
|||||||
Policy,
|
Policy,
|
||||||
Path,
|
Path,
|
||||||
)
|
)
|
||||||
from dbt.exceptions import InternalException
|
from dbt.exceptions import ApproximateMatch, InternalException, MultipleDatabasesNotAllowed
|
||||||
from dbt.node_types import NodeType
|
from dbt.node_types import NodeType
|
||||||
from dbt.utils import filter_null_values, deep_merge, classproperty
|
from dbt.utils import filter_null_values, deep_merge, classproperty
|
||||||
|
|
||||||
@@ -27,8 +26,10 @@ class BaseRelation(FakeAPIObject, Hashable):
|
|||||||
path: Path
|
path: Path
|
||||||
type: Optional[RelationType] = None
|
type: Optional[RelationType] = None
|
||||||
quote_character: str = '"'
|
quote_character: str = '"'
|
||||||
include_policy: Policy = Policy()
|
# Python 3.11 requires that these use default_factory instead of simple default
|
||||||
quote_policy: Policy = Policy()
|
# ValueError: mutable default <class 'dbt.contracts.relation.Policy'> for field include_policy is not allowed: use default_factory
|
||||||
|
include_policy: Policy = field(default_factory=lambda: Policy())
|
||||||
|
quote_policy: Policy = field(default_factory=lambda: Policy())
|
||||||
dbt_created: bool = False
|
dbt_created: bool = False
|
||||||
|
|
||||||
def _is_exactish_match(self, field: ComponentName, value: str) -> bool:
|
def _is_exactish_match(self, field: ComponentName, value: str) -> bool:
|
||||||
@@ -39,9 +40,9 @@ class BaseRelation(FakeAPIObject, Hashable):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_field_named(cls, field_name):
|
def _get_field_named(cls, field_name):
|
||||||
for field, _ in cls._get_fields():
|
for f, _ in cls._get_fields():
|
||||||
if field.name == field_name:
|
if f.name == field_name:
|
||||||
return field
|
return f
|
||||||
# this should be unreachable
|
# this should be unreachable
|
||||||
raise ValueError(f"BaseRelation has no {field_name} field!")
|
raise ValueError(f"BaseRelation has no {field_name} field!")
|
||||||
|
|
||||||
@@ -52,11 +53,11 @@ class BaseRelation(FakeAPIObject, Hashable):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_default_quote_policy(cls) -> Policy:
|
def get_default_quote_policy(cls) -> Policy:
|
||||||
return cls._get_field_named("quote_policy").default
|
return cls._get_field_named("quote_policy").default_factory()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_default_include_policy(cls) -> Policy:
|
def get_default_include_policy(cls) -> Policy:
|
||||||
return cls._get_field_named("include_policy").default
|
return cls._get_field_named("include_policy").default_factory()
|
||||||
|
|
||||||
def get(self, key, default=None):
|
def get(self, key, default=None):
|
||||||
"""Override `.get` to return a metadata object so we don't break
|
"""Override `.get` to return a metadata object so we don't break
|
||||||
@@ -99,7 +100,7 @@ class BaseRelation(FakeAPIObject, Hashable):
|
|||||||
|
|
||||||
if approximate_match and not exact_match:
|
if approximate_match and not exact_match:
|
||||||
target = self.create(database=database, schema=schema, identifier=identifier)
|
target = self.create(database=database, schema=schema, identifier=identifier)
|
||||||
dbt.exceptions.approximate_relation_match(target, self)
|
raise ApproximateMatch(target, self)
|
||||||
|
|
||||||
return exact_match
|
return exact_match
|
||||||
|
|
||||||
@@ -184,7 +185,7 @@ class BaseRelation(FakeAPIObject, Hashable):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_from_source(cls: Type[Self], source: ParsedSourceDefinition, **kwargs: Any) -> Self:
|
def create_from_source(cls: Type[Self], source: SourceDefinition, **kwargs: Any) -> Self:
|
||||||
source_quoting = source.quoting.to_dict(omit_none=True)
|
source_quoting = source.quoting.to_dict(omit_none=True)
|
||||||
source_quoting.pop("column", None)
|
source_quoting.pop("column", None)
|
||||||
quote_policy = deep_merge(
|
quote_policy = deep_merge(
|
||||||
@@ -209,7 +210,7 @@ class BaseRelation(FakeAPIObject, Hashable):
|
|||||||
def create_ephemeral_from_node(
|
def create_ephemeral_from_node(
|
||||||
cls: Type[Self],
|
cls: Type[Self],
|
||||||
config: HasQuoting,
|
config: HasQuoting,
|
||||||
node: Union[ParsedNode, CompiledNode],
|
node: ManifestNode,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
# Note that ephemeral models are based on the name.
|
# Note that ephemeral models are based on the name.
|
||||||
identifier = cls.add_ephemeral_prefix(node.name)
|
identifier = cls.add_ephemeral_prefix(node.name)
|
||||||
@@ -222,7 +223,7 @@ class BaseRelation(FakeAPIObject, Hashable):
|
|||||||
def create_from_node(
|
def create_from_node(
|
||||||
cls: Type[Self],
|
cls: Type[Self],
|
||||||
config: HasQuoting,
|
config: HasQuoting,
|
||||||
node: Union[ParsedNode, CompiledNode],
|
node: ManifestNode,
|
||||||
quote_policy: Optional[Dict[str, bool]] = None,
|
quote_policy: Optional[Dict[str, bool]] = None,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
@@ -243,20 +244,20 @@ class BaseRelation(FakeAPIObject, Hashable):
|
|||||||
def create_from(
|
def create_from(
|
||||||
cls: Type[Self],
|
cls: Type[Self],
|
||||||
config: HasQuoting,
|
config: HasQuoting,
|
||||||
node: Union[CompiledNode, ParsedNode, ParsedSourceDefinition],
|
node: ResultNode,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
if node.resource_type == NodeType.Source:
|
if node.resource_type == NodeType.Source:
|
||||||
if not isinstance(node, ParsedSourceDefinition):
|
if not isinstance(node, SourceDefinition):
|
||||||
raise InternalException(
|
raise InternalException(
|
||||||
"type mismatch, expected ParsedSourceDefinition but got {}".format(type(node))
|
"type mismatch, expected SourceDefinition but got {}".format(type(node))
|
||||||
)
|
)
|
||||||
return cls.create_from_source(node, **kwargs)
|
return cls.create_from_source(node, **kwargs)
|
||||||
else:
|
else:
|
||||||
if not isinstance(node, (ParsedNode, CompiledNode)):
|
# Can't use ManifestNode here because of parameterized generics
|
||||||
|
if not isinstance(node, (ParsedNode)):
|
||||||
raise InternalException(
|
raise InternalException(
|
||||||
"type mismatch, expected ParsedNode or CompiledNode but "
|
f"type mismatch, expected ManifestNode but got {type(node)}"
|
||||||
"got {}".format(type(node))
|
|
||||||
)
|
)
|
||||||
return cls.create_from_node(config, node, **kwargs)
|
return cls.create_from_node(config, node, **kwargs)
|
||||||
|
|
||||||
@@ -437,7 +438,7 @@ class SchemaSearchMap(Dict[InformationSchema, Set[Optional[str]]]):
|
|||||||
if not allow_multiple_databases:
|
if not allow_multiple_databases:
|
||||||
seen = {r.database.lower() for r in self if r.database}
|
seen = {r.database.lower() for r in self if r.database}
|
||||||
if len(seen) > 1:
|
if len(seen) > 1:
|
||||||
dbt.exceptions.raise_compiler_error(str(seen))
|
raise MultipleDatabasesNotAllowed(seen)
|
||||||
|
|
||||||
for information_schema_name, schema in self.search():
|
for information_schema_name, schema in self.search():
|
||||||
path = {"database": information_schema_name.database, "schema": schema}
|
path = {"database": information_schema_name.database, "schema": schema}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import re
|
|
||||||
import threading
|
import threading
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from typing import Any, Dict, Iterable, List, Optional, Set, Tuple
|
from typing import Any, Dict, Iterable, List, Optional, Set, Tuple
|
||||||
@@ -9,7 +8,13 @@ from dbt.adapters.reference_keys import (
|
|||||||
_make_msg_from_ref_key,
|
_make_msg_from_ref_key,
|
||||||
_ReferenceKey,
|
_ReferenceKey,
|
||||||
)
|
)
|
||||||
import dbt.exceptions
|
from dbt.exceptions import (
|
||||||
|
DependentLinkNotCached,
|
||||||
|
NewNameAlreadyInCache,
|
||||||
|
NoneRelationFound,
|
||||||
|
ReferencedLinkNotCached,
|
||||||
|
TruncatedModelNameCausedCollision,
|
||||||
|
)
|
||||||
from dbt.events.functions import fire_event, fire_event_if
|
from dbt.events.functions import fire_event, fire_event_if
|
||||||
from dbt.events.types import (
|
from dbt.events.types import (
|
||||||
AddLink,
|
AddLink,
|
||||||
@@ -150,11 +155,7 @@ class _CachedRelation:
|
|||||||
:raises InternalError: If the new key already exists.
|
:raises InternalError: If the new key already exists.
|
||||||
"""
|
"""
|
||||||
if new_key in self.referenced_by:
|
if new_key in self.referenced_by:
|
||||||
dbt.exceptions.raise_cache_inconsistent(
|
raise NewNameAlreadyInCache(old_key, new_key)
|
||||||
'in rename of "{}" -> "{}", new name is in the cache already'.format(
|
|
||||||
old_key, new_key
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if old_key not in self.referenced_by:
|
if old_key not in self.referenced_by:
|
||||||
return
|
return
|
||||||
@@ -270,15 +271,11 @@ class RelationsCache:
|
|||||||
if referenced is None:
|
if referenced is None:
|
||||||
return
|
return
|
||||||
if referenced is None:
|
if referenced is None:
|
||||||
dbt.exceptions.raise_cache_inconsistent(
|
raise ReferencedLinkNotCached(referenced_key)
|
||||||
"in add_link, referenced link key {} not in cache!".format(referenced_key)
|
|
||||||
)
|
|
||||||
|
|
||||||
dependent = self.relations.get(dependent_key)
|
dependent = self.relations.get(dependent_key)
|
||||||
if dependent is None:
|
if dependent is None:
|
||||||
dbt.exceptions.raise_cache_inconsistent(
|
raise DependentLinkNotCached(dependent_key)
|
||||||
"in add_link, dependent link key {} not in cache!".format(dependent_key)
|
|
||||||
)
|
|
||||||
|
|
||||||
assert dependent is not None # we just raised!
|
assert dependent is not None # we just raised!
|
||||||
|
|
||||||
@@ -430,24 +427,7 @@ class RelationsCache:
|
|||||||
if new_key in self.relations:
|
if new_key in self.relations:
|
||||||
# Tell user when collision caused by model names truncated during
|
# Tell user when collision caused by model names truncated during
|
||||||
# materialization.
|
# materialization.
|
||||||
match = re.search("__dbt_backup|__dbt_tmp$", new_key.identifier)
|
raise TruncatedModelNameCausedCollision(new_key, self.relations)
|
||||||
if match:
|
|
||||||
truncated_model_name_prefix = new_key.identifier[: match.start()]
|
|
||||||
message_addendum = (
|
|
||||||
"\n\nName collisions can occur when the length of two "
|
|
||||||
"models' names approach your database's builtin limit. "
|
|
||||||
"Try restructuring your project such that no two models "
|
|
||||||
"share the prefix '{}'.".format(truncated_model_name_prefix)
|
|
||||||
+ " Then, clean your warehouse of any removed models."
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
message_addendum = ""
|
|
||||||
|
|
||||||
dbt.exceptions.raise_cache_inconsistent(
|
|
||||||
"in rename, new key {} already in cache: {}{}".format(
|
|
||||||
new_key, list(self.relations.keys()), message_addendum
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if old_key not in self.relations:
|
if old_key not in self.relations:
|
||||||
fire_event(TemporaryRelation(key=_make_msg_from_ref_key(old_key)))
|
fire_event(TemporaryRelation(key=_make_msg_from_ref_key(old_key)))
|
||||||
@@ -505,9 +485,7 @@ class RelationsCache:
|
|||||||
]
|
]
|
||||||
|
|
||||||
if None in results:
|
if None in results:
|
||||||
dbt.exceptions.raise_cache_inconsistent(
|
raise NoneRelationFound()
|
||||||
"in get_relations, a None relation was found in the cache!"
|
|
||||||
)
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from typing import (
|
|||||||
Generic,
|
Generic,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
Tuple,
|
Tuple,
|
||||||
Union,
|
|
||||||
Dict,
|
Dict,
|
||||||
Any,
|
Any,
|
||||||
)
|
)
|
||||||
@@ -17,8 +16,7 @@ from typing_extensions import Protocol
|
|||||||
import agate
|
import agate
|
||||||
|
|
||||||
from dbt.contracts.connection import Connection, AdapterRequiredConfig, AdapterResponse
|
from dbt.contracts.connection import Connection, AdapterRequiredConfig, AdapterResponse
|
||||||
from dbt.contracts.graph.compiled import CompiledNode, ManifestNode, NonSourceCompiledNode
|
from dbt.contracts.graph.nodes import ResultNode, ManifestNode
|
||||||
from dbt.contracts.graph.parsed import ParsedNode, ParsedSourceDefinition
|
|
||||||
from dbt.contracts.graph.model_config import BaseConfig
|
from dbt.contracts.graph.model_config import BaseConfig
|
||||||
from dbt.contracts.graph.manifest import Manifest
|
from dbt.contracts.graph.manifest import Manifest
|
||||||
from dbt.contracts.relation import Policy, HasQuoting
|
from dbt.contracts.relation import Policy, HasQuoting
|
||||||
@@ -48,11 +46,7 @@ class RelationProtocol(Protocol):
|
|||||||
...
|
...
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_from(
|
def create_from(cls: Type[Self], config: HasQuoting, node: ResultNode) -> Self:
|
||||||
cls: Type[Self],
|
|
||||||
config: HasQuoting,
|
|
||||||
node: Union[CompiledNode, ParsedNode, ParsedSourceDefinition],
|
|
||||||
) -> Self:
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
@@ -65,7 +59,7 @@ class CompilerProtocol(Protocol):
|
|||||||
node: ManifestNode,
|
node: ManifestNode,
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
extra_context: Optional[Dict[str, Any]] = None,
|
extra_context: Optional[Dict[str, Any]] = None,
|
||||||
) -> NonSourceCompiledNode:
|
) -> ManifestNode:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from dbt.adapters.base import BaseConnectionManager
|
|||||||
from dbt.contracts.connection import Connection, ConnectionState, AdapterResponse
|
from dbt.contracts.connection import Connection, ConnectionState, AdapterResponse
|
||||||
from dbt.events.functions import fire_event
|
from dbt.events.functions import fire_event
|
||||||
from dbt.events.types import ConnectionUsed, SQLQuery, SQLCommit, SQLQueryStatus
|
from dbt.events.types import ConnectionUsed, SQLQuery, SQLCommit, SQLQueryStatus
|
||||||
|
from dbt.events.contextvars import get_node_info
|
||||||
from dbt.utils import cast_to_str
|
from dbt.utils import cast_to_str
|
||||||
|
|
||||||
|
|
||||||
@@ -56,7 +57,13 @@ class SQLConnectionManager(BaseConnectionManager):
|
|||||||
connection = self.get_thread_connection()
|
connection = self.get_thread_connection()
|
||||||
if auto_begin and connection.transaction_open is False:
|
if auto_begin and connection.transaction_open is False:
|
||||||
self.begin()
|
self.begin()
|
||||||
fire_event(ConnectionUsed(conn_type=self.TYPE, conn_name=cast_to_str(connection.name)))
|
fire_event(
|
||||||
|
ConnectionUsed(
|
||||||
|
conn_type=self.TYPE,
|
||||||
|
conn_name=cast_to_str(connection.name),
|
||||||
|
node_info=get_node_info(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
with self.exception_handler(sql):
|
with self.exception_handler(sql):
|
||||||
if abridge_sql_log:
|
if abridge_sql_log:
|
||||||
@@ -64,7 +71,11 @@ class SQLConnectionManager(BaseConnectionManager):
|
|||||||
else:
|
else:
|
||||||
log_sql = sql
|
log_sql = sql
|
||||||
|
|
||||||
fire_event(SQLQuery(conn_name=cast_to_str(connection.name), sql=log_sql))
|
fire_event(
|
||||||
|
SQLQuery(
|
||||||
|
conn_name=cast_to_str(connection.name), sql=log_sql, node_info=get_node_info()
|
||||||
|
)
|
||||||
|
)
|
||||||
pre = time.time()
|
pre = time.time()
|
||||||
|
|
||||||
cursor = connection.handle.cursor()
|
cursor = connection.handle.cursor()
|
||||||
@@ -72,7 +83,9 @@ class SQLConnectionManager(BaseConnectionManager):
|
|||||||
|
|
||||||
fire_event(
|
fire_event(
|
||||||
SQLQueryStatus(
|
SQLQueryStatus(
|
||||||
status=str(self.get_response(cursor)), elapsed=round((time.time() - pre), 2)
|
status=str(self.get_response(cursor)),
|
||||||
|
elapsed=round((time.time() - pre)),
|
||||||
|
node_info=get_node_info(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -156,7 +169,7 @@ class SQLConnectionManager(BaseConnectionManager):
|
|||||||
"it does not have one open!".format(connection.name)
|
"it does not have one open!".format(connection.name)
|
||||||
)
|
)
|
||||||
|
|
||||||
fire_event(SQLCommit(conn_name=connection.name))
|
fire_event(SQLCommit(conn_name=connection.name, node_info=get_node_info()))
|
||||||
self.add_commit_query()
|
self.add_commit_query()
|
||||||
|
|
||||||
connection.transaction_open = False
|
connection.transaction_open = False
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import agate
|
import agate
|
||||||
from typing import Any, Optional, Tuple, Type, List
|
from typing import Any, Optional, Tuple, Type, List
|
||||||
|
|
||||||
import dbt.clients.agate_helper
|
|
||||||
from dbt.contracts.connection import Connection
|
from dbt.contracts.connection import Connection
|
||||||
import dbt.exceptions
|
from dbt.exceptions import RelationTypeNull
|
||||||
from dbt.adapters.base import BaseAdapter, available
|
from dbt.adapters.base import BaseAdapter, available
|
||||||
from dbt.adapters.cache import _make_ref_key_msg
|
from dbt.adapters.cache import _make_ref_key_msg
|
||||||
from dbt.adapters.sql import SQLConnectionManager
|
from dbt.adapters.sql import SQLConnectionManager
|
||||||
@@ -132,9 +131,7 @@ class SQLAdapter(BaseAdapter):
|
|||||||
|
|
||||||
def drop_relation(self, relation):
|
def drop_relation(self, relation):
|
||||||
if relation.type is None:
|
if relation.type is None:
|
||||||
dbt.exceptions.raise_compiler_error(
|
raise RelationTypeNull(relation)
|
||||||
"Tried to drop relation {}, but its type is null.".format(relation)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.cache_dropped(relation)
|
self.cache_dropped(relation)
|
||||||
self.execute_macro(DROP_RELATION_MACRO_NAME, kwargs={"relation": relation})
|
self.execute_macro(DROP_RELATION_MACRO_NAME, kwargs={"relation": relation})
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ class dbtRunner:
|
|||||||
@p.cache_selected_only
|
@p.cache_selected_only
|
||||||
@p.debug
|
@p.debug
|
||||||
@p.enable_legacy_logger
|
@p.enable_legacy_logger
|
||||||
@p.event_buffer_size
|
|
||||||
@p.fail_fast
|
@p.fail_fast
|
||||||
@p.log_cache_events
|
@p.log_cache_events
|
||||||
@p.log_format
|
@p.log_format
|
||||||
|
|||||||
@@ -80,14 +80,6 @@ enable_legacy_logger = click.option(
|
|||||||
hidden=True,
|
hidden=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
event_buffer_size = click.option(
|
|
||||||
"--event-buffer-size",
|
|
||||||
envvar="DBT_EVENT_BUFFER_SIZE",
|
|
||||||
help="Sets the max number of events to buffer in EVENT_HISTORY.",
|
|
||||||
default=100000,
|
|
||||||
type=click.INT,
|
|
||||||
)
|
|
||||||
|
|
||||||
exclude = click.option("--exclude", envvar=None, help="Specify the nodes to exclude.")
|
exclude = click.option("--exclude", envvar=None, help="Specify the nodes to exclude.")
|
||||||
|
|
||||||
fail_fast = click.option(
|
fail_fast = click.option(
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
import re
|
import re
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
import dbt.exceptions
|
from dbt.exceptions import (
|
||||||
|
BlockDefinitionNotAtTop,
|
||||||
|
InternalException,
|
||||||
|
MissingCloseTag,
|
||||||
|
MissingControlFlowStartTag,
|
||||||
|
NestedTags,
|
||||||
|
UnexpectedControlFlowEndTag,
|
||||||
|
UnexpectedMacroEOF,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def regex(pat):
|
def regex(pat):
|
||||||
@@ -139,10 +147,7 @@ class TagIterator:
|
|||||||
def _expect_match(self, expected_name, *patterns, **kwargs):
|
def _expect_match(self, expected_name, *patterns, **kwargs):
|
||||||
match = self._first_match(*patterns, **kwargs)
|
match = self._first_match(*patterns, **kwargs)
|
||||||
if match is None:
|
if match is None:
|
||||||
msg = 'unexpected EOF, expected {}, got "{}"'.format(
|
raise UnexpectedMacroEOF(expected_name, self.data[self.pos :])
|
||||||
expected_name, self.data[self.pos :]
|
|
||||||
)
|
|
||||||
dbt.exceptions.raise_compiler_error(msg)
|
|
||||||
return match
|
return match
|
||||||
|
|
||||||
def handle_expr(self, match):
|
def handle_expr(self, match):
|
||||||
@@ -256,7 +261,7 @@ class TagIterator:
|
|||||||
elif block_type_name is not None:
|
elif block_type_name is not None:
|
||||||
yield self.handle_tag(match)
|
yield self.handle_tag(match)
|
||||||
else:
|
else:
|
||||||
raise dbt.exceptions.InternalException(
|
raise InternalException(
|
||||||
"Invalid regex match in next_block, expected block start, "
|
"Invalid regex match in next_block, expected block start, "
|
||||||
"expr start, or comment start"
|
"expr start, or comment start"
|
||||||
)
|
)
|
||||||
@@ -265,13 +270,6 @@ class TagIterator:
|
|||||||
return self.find_tags()
|
return self.find_tags()
|
||||||
|
|
||||||
|
|
||||||
duplicate_tags = (
|
|
||||||
"Got nested tags: {outer.block_type_name} (started at {outer.start}) did "
|
|
||||||
"not have a matching {{% end{outer.block_type_name} %}} before a "
|
|
||||||
"subsequent {inner.block_type_name} was found (started at {inner.start})"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
_CONTROL_FLOW_TAGS = {
|
_CONTROL_FLOW_TAGS = {
|
||||||
"if": "endif",
|
"if": "endif",
|
||||||
"for": "endfor",
|
"for": "endfor",
|
||||||
@@ -319,33 +317,16 @@ class BlockIterator:
|
|||||||
found = self.stack.pop()
|
found = self.stack.pop()
|
||||||
else:
|
else:
|
||||||
expected = _CONTROL_FLOW_END_TAGS[tag.block_type_name]
|
expected = _CONTROL_FLOW_END_TAGS[tag.block_type_name]
|
||||||
dbt.exceptions.raise_compiler_error(
|
raise UnexpectedControlFlowEndTag(tag, expected, self.tag_parser)
|
||||||
(
|
|
||||||
"Got an unexpected control flow end tag, got {} but "
|
|
||||||
"never saw a preceeding {} (@ {})"
|
|
||||||
).format(tag.block_type_name, expected, self.tag_parser.linepos(tag.start))
|
|
||||||
)
|
|
||||||
expected = _CONTROL_FLOW_TAGS[found]
|
expected = _CONTROL_FLOW_TAGS[found]
|
||||||
if expected != tag.block_type_name:
|
if expected != tag.block_type_name:
|
||||||
dbt.exceptions.raise_compiler_error(
|
raise MissingControlFlowStartTag(tag, expected, self.tag_parser)
|
||||||
(
|
|
||||||
"Got an unexpected control flow end tag, got {} but "
|
|
||||||
"expected {} next (@ {})"
|
|
||||||
).format(tag.block_type_name, expected, self.tag_parser.linepos(tag.start))
|
|
||||||
)
|
|
||||||
|
|
||||||
if tag.block_type_name in allowed_blocks:
|
if tag.block_type_name in allowed_blocks:
|
||||||
if self.stack:
|
if self.stack:
|
||||||
dbt.exceptions.raise_compiler_error(
|
raise BlockDefinitionNotAtTop(self.tag_parser, tag.start)
|
||||||
(
|
|
||||||
"Got a block definition inside control flow at {}. "
|
|
||||||
"All dbt block definitions must be at the top level"
|
|
||||||
).format(self.tag_parser.linepos(tag.start))
|
|
||||||
)
|
|
||||||
if self.current is not None:
|
if self.current is not None:
|
||||||
dbt.exceptions.raise_compiler_error(
|
raise NestedTags(outer=self.current, inner=tag)
|
||||||
duplicate_tags.format(outer=self.current, inner=tag)
|
|
||||||
)
|
|
||||||
if collect_raw_data:
|
if collect_raw_data:
|
||||||
raw_data = self.data[self.last_position : tag.start]
|
raw_data = self.data[self.last_position : tag.start]
|
||||||
self.last_position = tag.start
|
self.last_position = tag.start
|
||||||
@@ -366,11 +347,7 @@ class BlockIterator:
|
|||||||
|
|
||||||
if self.current:
|
if self.current:
|
||||||
linecount = self.data[: self.current.end].count("\n") + 1
|
linecount = self.data[: self.current.end].count("\n") + 1
|
||||||
dbt.exceptions.raise_compiler_error(
|
raise MissingCloseTag(self.current.block_type_name, linecount)
|
||||||
("Reached EOF without finding a close tag for {} (searched from line {})").format(
|
|
||||||
self.current.block_type_name, linecount
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if collect_raw_data:
|
if collect_raw_data:
|
||||||
raw_data = self.data[self.last_position :]
|
raw_data = self.data[self.last_position :]
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ from dbt.events.types import (
|
|||||||
)
|
)
|
||||||
from dbt.exceptions import (
|
from dbt.exceptions import (
|
||||||
CommandResultError,
|
CommandResultError,
|
||||||
|
GitCheckoutError,
|
||||||
|
GitCloningError,
|
||||||
|
GitCloningProblem,
|
||||||
RuntimeException,
|
RuntimeException,
|
||||||
bad_package_spec,
|
|
||||||
raise_git_cloning_error,
|
|
||||||
raise_git_cloning_problem,
|
|
||||||
)
|
)
|
||||||
from packaging import version
|
from packaging import version
|
||||||
|
|
||||||
@@ -27,16 +27,6 @@ def _is_commit(revision: str) -> bool:
|
|||||||
return bool(re.match(r"\b[0-9a-f]{40}\b", revision))
|
return bool(re.match(r"\b[0-9a-f]{40}\b", revision))
|
||||||
|
|
||||||
|
|
||||||
def _raise_git_cloning_error(repo, revision, error):
|
|
||||||
stderr = error.stderr.strip()
|
|
||||||
if "usage: git" in stderr:
|
|
||||||
stderr = stderr.split("\nusage: git")[0]
|
|
||||||
if re.match("fatal: destination path '(.+)' already exists", stderr):
|
|
||||||
raise_git_cloning_error(error)
|
|
||||||
|
|
||||||
bad_package_spec(repo, revision, stderr)
|
|
||||||
|
|
||||||
|
|
||||||
def clone(repo, cwd, dirname=None, remove_git_dir=False, revision=None, subdirectory=None):
|
def clone(repo, cwd, dirname=None, remove_git_dir=False, revision=None, subdirectory=None):
|
||||||
has_revision = revision is not None
|
has_revision = revision is not None
|
||||||
is_commit = _is_commit(revision or "")
|
is_commit = _is_commit(revision or "")
|
||||||
@@ -64,7 +54,7 @@ def clone(repo, cwd, dirname=None, remove_git_dir=False, revision=None, subdirec
|
|||||||
try:
|
try:
|
||||||
result = run_cmd(cwd, clone_cmd, env={"LC_ALL": "C"})
|
result = run_cmd(cwd, clone_cmd, env={"LC_ALL": "C"})
|
||||||
except CommandResultError as exc:
|
except CommandResultError as exc:
|
||||||
_raise_git_cloning_error(repo, revision, exc)
|
raise GitCloningError(repo, revision, exc)
|
||||||
|
|
||||||
if subdirectory:
|
if subdirectory:
|
||||||
cwd_subdir = os.path.join(cwd, dirname or "")
|
cwd_subdir = os.path.join(cwd, dirname or "")
|
||||||
@@ -72,7 +62,7 @@ def clone(repo, cwd, dirname=None, remove_git_dir=False, revision=None, subdirec
|
|||||||
try:
|
try:
|
||||||
run_cmd(cwd_subdir, clone_cmd_subdir)
|
run_cmd(cwd_subdir, clone_cmd_subdir)
|
||||||
except CommandResultError as exc:
|
except CommandResultError as exc:
|
||||||
_raise_git_cloning_error(repo, revision, exc)
|
raise GitCloningError(repo, revision, exc)
|
||||||
|
|
||||||
if remove_git_dir:
|
if remove_git_dir:
|
||||||
rmdir(os.path.join(dirname, ".git"))
|
rmdir(os.path.join(dirname, ".git"))
|
||||||
@@ -115,8 +105,7 @@ def checkout(cwd, repo, revision=None):
|
|||||||
try:
|
try:
|
||||||
return _checkout(cwd, repo, revision)
|
return _checkout(cwd, repo, revision)
|
||||||
except CommandResultError as exc:
|
except CommandResultError as exc:
|
||||||
stderr = exc.stderr.strip()
|
raise GitCheckoutError(repo=repo, revision=revision, error=exc)
|
||||||
bad_package_spec(repo, revision, stderr)
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_sha(cwd):
|
def get_current_sha(cwd):
|
||||||
@@ -145,7 +134,7 @@ def clone_and_checkout(
|
|||||||
err = exc.stderr
|
err = exc.stderr
|
||||||
exists = re.match("fatal: destination path '(.+)' already exists", err)
|
exists = re.match("fatal: destination path '(.+)' already exists", err)
|
||||||
if not exists:
|
if not exists:
|
||||||
raise_git_cloning_problem(repo)
|
raise GitCloningProblem(repo)
|
||||||
|
|
||||||
directory = None
|
directory = None
|
||||||
start_sha = None
|
start_sha = None
|
||||||
|
|||||||
@@ -25,16 +25,19 @@ from dbt.utils import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from dbt.clients._jinja_blocks import BlockIterator, BlockData, BlockTag
|
from dbt.clients._jinja_blocks import BlockIterator, BlockData, BlockTag
|
||||||
from dbt.contracts.graph.compiled import CompiledGenericTestNode
|
from dbt.contracts.graph.nodes import GenericTestNode
|
||||||
from dbt.contracts.graph.parsed import ParsedGenericTestNode
|
|
||||||
|
|
||||||
from dbt.exceptions import (
|
from dbt.exceptions import (
|
||||||
InternalException,
|
CaughtMacroException,
|
||||||
raise_compiler_error,
|
CaughtMacroExceptionWithNode,
|
||||||
CompilationException,
|
CompilationException,
|
||||||
invalid_materialization_argument,
|
InternalException,
|
||||||
MacroReturn,
|
InvalidMaterializationArg,
|
||||||
JinjaRenderingException,
|
JinjaRenderingException,
|
||||||
|
MacroReturn,
|
||||||
|
MaterializtionMacroNotUsed,
|
||||||
|
NoSupportedLanguagesFound,
|
||||||
|
UndefinedCompilation,
|
||||||
UndefinedMacroException,
|
UndefinedMacroException,
|
||||||
)
|
)
|
||||||
from dbt import flags
|
from dbt import flags
|
||||||
@@ -238,7 +241,7 @@ class BaseMacroGenerator:
|
|||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
except (TypeError, jinja2.exceptions.TemplateRuntimeError) as e:
|
except (TypeError, jinja2.exceptions.TemplateRuntimeError) as e:
|
||||||
raise_compiler_error(str(e))
|
raise CaughtMacroException(e)
|
||||||
|
|
||||||
def call_macro(self, *args, **kwargs):
|
def call_macro(self, *args, **kwargs):
|
||||||
# called from __call__ methods
|
# called from __call__ methods
|
||||||
@@ -297,7 +300,7 @@ class MacroGenerator(BaseMacroGenerator):
|
|||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
except (TypeError, jinja2.exceptions.TemplateRuntimeError) as e:
|
except (TypeError, jinja2.exceptions.TemplateRuntimeError) as e:
|
||||||
raise_compiler_error(str(e), self.macro)
|
raise CaughtMacroExceptionWithNode(exc=e, node=self.macro)
|
||||||
except CompilationException as e:
|
except CompilationException as e:
|
||||||
e.stack.append(self.macro)
|
e.stack.append(self.macro)
|
||||||
raise e
|
raise e
|
||||||
@@ -377,7 +380,7 @@ class MaterializationExtension(jinja2.ext.Extension):
|
|||||||
node.defaults.append(languages)
|
node.defaults.append(languages)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
invalid_materialization_argument(materialization_name, target.name)
|
raise InvalidMaterializationArg(materialization_name, target.name)
|
||||||
|
|
||||||
if SUPPORTED_LANG_ARG not in node.args:
|
if SUPPORTED_LANG_ARG not in node.args:
|
||||||
node.args.append(SUPPORTED_LANG_ARG)
|
node.args.append(SUPPORTED_LANG_ARG)
|
||||||
@@ -452,7 +455,7 @@ def create_undefined(node=None):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def __reduce__(self):
|
def __reduce__(self):
|
||||||
raise_compiler_error(f"{self.name} is undefined", node=node)
|
raise UndefinedCompilation(name=self.name, node=node)
|
||||||
|
|
||||||
return Undefined
|
return Undefined
|
||||||
|
|
||||||
@@ -620,7 +623,7 @@ GENERIC_TEST_KWARGS_NAME = "_dbt_generic_test_kwargs"
|
|||||||
|
|
||||||
def add_rendered_test_kwargs(
|
def add_rendered_test_kwargs(
|
||||||
context: Dict[str, Any],
|
context: Dict[str, Any],
|
||||||
node: Union[ParsedGenericTestNode, CompiledGenericTestNode],
|
node: GenericTestNode,
|
||||||
capture_macros: bool = False,
|
capture_macros: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Render each of the test kwargs in the given context using the native
|
"""Render each of the test kwargs in the given context using the native
|
||||||
@@ -652,13 +655,13 @@ def add_rendered_test_kwargs(
|
|||||||
|
|
||||||
def get_supported_languages(node: jinja2.nodes.Macro) -> List[ModelLanguage]:
|
def get_supported_languages(node: jinja2.nodes.Macro) -> List[ModelLanguage]:
|
||||||
if "materialization" not in node.name:
|
if "materialization" not in node.name:
|
||||||
raise_compiler_error("Only materialization macros can be used with this function")
|
raise MaterializtionMacroNotUsed(node=node)
|
||||||
|
|
||||||
no_kwargs = not node.defaults
|
no_kwargs = not node.defaults
|
||||||
no_langs_found = SUPPORTED_LANG_ARG not in node.args
|
no_langs_found = SUPPORTED_LANG_ARG not in node.args
|
||||||
|
|
||||||
if no_kwargs or no_langs_found:
|
if no_kwargs or no_langs_found:
|
||||||
raise_compiler_error(f"No supported_languages found in materialization macro {node.name}")
|
raise NoSupportedLanguagesFound(node=node)
|
||||||
|
|
||||||
lang_idx = node.args.index(SUPPORTED_LANG_ARG)
|
lang_idx = node.args.index(SUPPORTED_LANG_ARG)
|
||||||
# indexing defaults from the end
|
# indexing defaults from the end
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import jinja2
|
import jinja2
|
||||||
from dbt.clients.jinja import get_environment
|
from dbt.clients.jinja import get_environment
|
||||||
from dbt.exceptions import raise_compiler_error
|
from dbt.exceptions import MacroNamespaceNotString, MacroNameNotString
|
||||||
|
|
||||||
|
|
||||||
def statically_extract_macro_calls(string, ctx, db_wrapper=None):
|
def statically_extract_macro_calls(string, ctx, db_wrapper=None):
|
||||||
@@ -117,20 +117,14 @@ def statically_parse_adapter_dispatch(func_call, ctx, db_wrapper):
|
|||||||
func_name = kwarg.value.value
|
func_name = kwarg.value.value
|
||||||
possible_macro_calls.append(func_name)
|
possible_macro_calls.append(func_name)
|
||||||
else:
|
else:
|
||||||
raise_compiler_error(
|
raise MacroNameNotString(kwarg_value=kwarg.value.value)
|
||||||
f"The macro_name parameter ({kwarg.value.value}) "
|
|
||||||
"to adapter.dispatch was not a string"
|
|
||||||
)
|
|
||||||
elif kwarg.key == "macro_namespace":
|
elif kwarg.key == "macro_namespace":
|
||||||
# This will remain to enable static resolution
|
# This will remain to enable static resolution
|
||||||
kwarg_type = type(kwarg.value).__name__
|
kwarg_type = type(kwarg.value).__name__
|
||||||
if kwarg_type == "Const":
|
if kwarg_type == "Const":
|
||||||
macro_namespace = kwarg.value.value
|
macro_namespace = kwarg.value.value
|
||||||
else:
|
else:
|
||||||
raise_compiler_error(
|
raise MacroNamespaceNotString(kwarg_type)
|
||||||
"The macro_namespace parameter to adapter.dispatch "
|
|
||||||
f"is a {kwarg_type}, not a string"
|
|
||||||
)
|
|
||||||
|
|
||||||
# positional arguments
|
# positional arguments
|
||||||
if packages_arg:
|
if packages_arg:
|
||||||
|
|||||||
@@ -157,7 +157,8 @@ def make_symlink(source: str, link_path: str) -> None:
|
|||||||
Create a symlink at `link_path` referring to `source`.
|
Create a symlink at `link_path` referring to `source`.
|
||||||
"""
|
"""
|
||||||
if not supports_symlinks():
|
if not supports_symlinks():
|
||||||
dbt.exceptions.system_error("create a symbolic link")
|
# TODO: why not import these at top?
|
||||||
|
raise dbt.exceptions.SymbolicLinkError()
|
||||||
|
|
||||||
os.symlink(source, link_path)
|
os.symlink(source, link_path)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import List, Dict, Any, Tuple, cast, Optional
|
from typing import List, Dict, Any, Tuple, Optional
|
||||||
|
|
||||||
import networkx as nx # type: ignore
|
import networkx as nx # type: ignore
|
||||||
import pickle
|
import pickle
|
||||||
@@ -12,23 +12,23 @@ from dbt.clients import jinja
|
|||||||
from dbt.clients.system import make_directory
|
from dbt.clients.system import make_directory
|
||||||
from dbt.context.providers import generate_runtime_model_context
|
from dbt.context.providers import generate_runtime_model_context
|
||||||
from dbt.contracts.graph.manifest import Manifest, UniqueID
|
from dbt.contracts.graph.manifest import Manifest, UniqueID
|
||||||
from dbt.contracts.graph.compiled import (
|
from dbt.contracts.graph.nodes import (
|
||||||
COMPILED_TYPES,
|
ManifestNode,
|
||||||
CompiledGenericTestNode,
|
ManifestSQLNode,
|
||||||
|
GenericTestNode,
|
||||||
GraphMemberNode,
|
GraphMemberNode,
|
||||||
InjectedCTE,
|
InjectedCTE,
|
||||||
ManifestNode,
|
SeedNode,
|
||||||
NonSourceCompiledNode,
|
|
||||||
)
|
)
|
||||||
from dbt.contracts.graph.parsed import ParsedNode
|
|
||||||
from dbt.exceptions import (
|
from dbt.exceptions import (
|
||||||
dependency_not_found,
|
GraphDependencyNotFound,
|
||||||
InternalException,
|
InternalException,
|
||||||
RuntimeException,
|
RuntimeException,
|
||||||
)
|
)
|
||||||
from dbt.graph import Graph
|
from dbt.graph import Graph
|
||||||
from dbt.events.functions import fire_event
|
from dbt.events.functions import fire_event
|
||||||
from dbt.events.types import FoundStats, CompilingNode, WritingInjectedSQLForNode
|
from dbt.events.types import FoundStats, WritingInjectedSQLForNode
|
||||||
|
from dbt.events.contextvars import get_node_info
|
||||||
from dbt.node_types import NodeType, ModelLanguage
|
from dbt.node_types import NodeType, ModelLanguage
|
||||||
from dbt.events.format import pluralize
|
from dbt.events.format import pluralize
|
||||||
import dbt.tracking
|
import dbt.tracking
|
||||||
@@ -36,14 +36,6 @@ import dbt.tracking
|
|||||||
graph_file_name = "graph.gpickle"
|
graph_file_name = "graph.gpickle"
|
||||||
|
|
||||||
|
|
||||||
def _compiled_type_for(model: ParsedNode):
|
|
||||||
if type(model) not in COMPILED_TYPES:
|
|
||||||
raise InternalException(
|
|
||||||
f"Asked to compile {type(model)} node, but it has no compiled form"
|
|
||||||
)
|
|
||||||
return COMPILED_TYPES[type(model)]
|
|
||||||
|
|
||||||
|
|
||||||
def print_compile_stats(stats):
|
def print_compile_stats(stats):
|
||||||
names = {
|
names = {
|
||||||
NodeType.Model: "model",
|
NodeType.Model: "model",
|
||||||
@@ -176,7 +168,7 @@ class Compiler:
|
|||||||
# a dict for jinja rendering of SQL
|
# a dict for jinja rendering of SQL
|
||||||
def _create_node_context(
|
def _create_node_context(
|
||||||
self,
|
self,
|
||||||
node: NonSourceCompiledNode,
|
node: ManifestSQLNode,
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
extra_context: Dict[str, Any],
|
extra_context: Dict[str, Any],
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
@@ -184,7 +176,7 @@ class Compiler:
|
|||||||
context = generate_runtime_model_context(node, self.config, manifest)
|
context = generate_runtime_model_context(node, self.config, manifest)
|
||||||
context.update(extra_context)
|
context.update(extra_context)
|
||||||
|
|
||||||
if isinstance(node, CompiledGenericTestNode):
|
if isinstance(node, GenericTestNode):
|
||||||
# for test nodes, add a special keyword args value to the context
|
# for test nodes, add a special keyword args value to the context
|
||||||
jinja.add_rendered_test_kwargs(context, node)
|
jinja.add_rendered_test_kwargs(context, node)
|
||||||
|
|
||||||
@@ -195,14 +187,6 @@ class Compiler:
|
|||||||
relation_cls = adapter.Relation
|
relation_cls = adapter.Relation
|
||||||
return relation_cls.add_ephemeral_prefix(name)
|
return relation_cls.add_ephemeral_prefix(name)
|
||||||
|
|
||||||
def _get_relation_name(self, node: ParsedNode):
|
|
||||||
relation_name = None
|
|
||||||
if node.is_relational and not node.is_ephemeral_model:
|
|
||||||
adapter = get_adapter(self.config)
|
|
||||||
relation_cls = adapter.Relation
|
|
||||||
relation_name = str(relation_cls.create_from(self.config, node))
|
|
||||||
return relation_name
|
|
||||||
|
|
||||||
def _inject_ctes_into_sql(self, sql: str, ctes: List[InjectedCTE]) -> str:
|
def _inject_ctes_into_sql(self, sql: str, ctes: List[InjectedCTE]) -> str:
|
||||||
"""
|
"""
|
||||||
`ctes` is a list of InjectedCTEs like:
|
`ctes` is a list of InjectedCTEs like:
|
||||||
@@ -261,10 +245,10 @@ class Compiler:
|
|||||||
|
|
||||||
def _recursively_prepend_ctes(
|
def _recursively_prepend_ctes(
|
||||||
self,
|
self,
|
||||||
model: NonSourceCompiledNode,
|
model: ManifestSQLNode,
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
extra_context: Optional[Dict[str, Any]],
|
extra_context: Optional[Dict[str, Any]],
|
||||||
) -> Tuple[NonSourceCompiledNode, List[InjectedCTE]]:
|
) -> Tuple[ManifestSQLNode, List[InjectedCTE]]:
|
||||||
"""This method is called by the 'compile_node' method. Starting
|
"""This method is called by the 'compile_node' method. Starting
|
||||||
from the node that it is passed in, it will recursively call
|
from the node that it is passed in, it will recursively call
|
||||||
itself using the 'extra_ctes'. The 'ephemeral' models do
|
itself using the 'extra_ctes'. The 'ephemeral' models do
|
||||||
@@ -279,6 +263,7 @@ class Compiler:
|
|||||||
|
|
||||||
# Just to make it plain that nothing is actually injected for this case
|
# Just to make it plain that nothing is actually injected for this case
|
||||||
if not model.extra_ctes:
|
if not model.extra_ctes:
|
||||||
|
if not isinstance(model, SeedNode):
|
||||||
model.extra_ctes_injected = True
|
model.extra_ctes_injected = True
|
||||||
manifest.update_node(model)
|
manifest.update_node(model)
|
||||||
return (model, model.extra_ctes)
|
return (model, model.extra_ctes)
|
||||||
@@ -298,6 +283,7 @@ class Compiler:
|
|||||||
f"could not be resolved: {cte.id}"
|
f"could not be resolved: {cte.id}"
|
||||||
)
|
)
|
||||||
cte_model = manifest.nodes[cte.id]
|
cte_model = manifest.nodes[cte.id]
|
||||||
|
assert not isinstance(cte_model, SeedNode)
|
||||||
|
|
||||||
if not cte_model.is_ephemeral_model:
|
if not cte_model.is_ephemeral_model:
|
||||||
raise InternalException(f"{cte.id} is not ephemeral")
|
raise InternalException(f"{cte.id} is not ephemeral")
|
||||||
@@ -305,8 +291,6 @@ class Compiler:
|
|||||||
# This model has already been compiled, so it's been
|
# This model has already been compiled, so it's been
|
||||||
# through here before
|
# through here before
|
||||||
if getattr(cte_model, "compiled", False):
|
if getattr(cte_model, "compiled", False):
|
||||||
assert isinstance(cte_model, tuple(COMPILED_TYPES.values()))
|
|
||||||
cte_model = cast(NonSourceCompiledNode, cte_model)
|
|
||||||
new_prepended_ctes = cte_model.extra_ctes
|
new_prepended_ctes = cte_model.extra_ctes
|
||||||
|
|
||||||
# if the cte_model isn't compiled, i.e. first time here
|
# if the cte_model isn't compiled, i.e. first time here
|
||||||
@@ -343,21 +327,19 @@ class Compiler:
|
|||||||
|
|
||||||
return model, prepended_ctes
|
return model, prepended_ctes
|
||||||
|
|
||||||
# creates a compiled_node from the ManifestNode passed in,
|
# Sets compiled fields in the ManifestSQLNode passed in,
|
||||||
# creates a "context" dictionary for jinja rendering,
|
# creates a "context" dictionary for jinja rendering,
|
||||||
# and then renders the "compiled_code" using the node, the
|
# and then renders the "compiled_code" using the node, the
|
||||||
# raw_code and the context.
|
# raw_code and the context.
|
||||||
def _compile_node(
|
def _compile_node(
|
||||||
self,
|
self,
|
||||||
node: ManifestNode,
|
node: ManifestSQLNode,
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
extra_context: Optional[Dict[str, Any]] = None,
|
extra_context: Optional[Dict[str, Any]] = None,
|
||||||
) -> NonSourceCompiledNode:
|
) -> ManifestSQLNode:
|
||||||
if extra_context is None:
|
if extra_context is None:
|
||||||
extra_context = {}
|
extra_context = {}
|
||||||
|
|
||||||
fire_event(CompilingNode(unique_id=node.unique_id))
|
|
||||||
|
|
||||||
data = node.to_dict(omit_none=True)
|
data = node.to_dict(omit_none=True)
|
||||||
data.update(
|
data.update(
|
||||||
{
|
{
|
||||||
@@ -367,9 +349,8 @@ class Compiler:
|
|||||||
"extra_ctes": [],
|
"extra_ctes": [],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
compiled_node = _compiled_type_for(node).from_dict(data)
|
|
||||||
|
|
||||||
if compiled_node.language == ModelLanguage.python:
|
if node.language == ModelLanguage.python:
|
||||||
# TODO could we also 'minify' this code at all? just aesthetic, not functional
|
# TODO could we also 'minify' this code at all? just aesthetic, not functional
|
||||||
|
|
||||||
# quoating seems like something very specific to sql so far
|
# quoating seems like something very specific to sql so far
|
||||||
@@ -377,7 +358,7 @@ class Compiler:
|
|||||||
# TODO try to find better way to do this, given that
|
# TODO try to find better way to do this, given that
|
||||||
original_quoting = self.config.quoting
|
original_quoting = self.config.quoting
|
||||||
self.config.quoting = {key: False for key in original_quoting.keys()}
|
self.config.quoting = {key: False for key in original_quoting.keys()}
|
||||||
context = self._create_node_context(compiled_node, manifest, extra_context)
|
context = self._create_node_context(node, manifest, extra_context)
|
||||||
|
|
||||||
postfix = jinja.get_rendered(
|
postfix = jinja.get_rendered(
|
||||||
"{{ py_script_postfix(model) }}",
|
"{{ py_script_postfix(model) }}",
|
||||||
@@ -385,23 +366,21 @@ class Compiler:
|
|||||||
node,
|
node,
|
||||||
)
|
)
|
||||||
# we should NOT jinja render the python model's 'raw code'
|
# we should NOT jinja render the python model's 'raw code'
|
||||||
compiled_node.compiled_code = f"{node.raw_code}\n\n{postfix}"
|
node.compiled_code = f"{node.raw_code}\n\n{postfix}"
|
||||||
# restore quoting settings in the end since context is lazy evaluated
|
# restore quoting settings in the end since context is lazy evaluated
|
||||||
self.config.quoting = original_quoting
|
self.config.quoting = original_quoting
|
||||||
|
|
||||||
else:
|
else:
|
||||||
context = self._create_node_context(compiled_node, manifest, extra_context)
|
context = self._create_node_context(node, manifest, extra_context)
|
||||||
compiled_node.compiled_code = jinja.get_rendered(
|
node.compiled_code = jinja.get_rendered(
|
||||||
node.raw_code,
|
node.raw_code,
|
||||||
context,
|
context,
|
||||||
node,
|
node,
|
||||||
)
|
)
|
||||||
|
|
||||||
compiled_node.relation_name = self._get_relation_name(node)
|
node.compiled = True
|
||||||
|
|
||||||
compiled_node.compiled = True
|
return node
|
||||||
|
|
||||||
return compiled_node
|
|
||||||
|
|
||||||
def write_graph_file(self, linker: Linker, manifest: Manifest):
|
def write_graph_file(self, linker: Linker, manifest: Manifest):
|
||||||
filename = graph_file_name
|
filename = graph_file_name
|
||||||
@@ -420,7 +399,7 @@ class Compiler:
|
|||||||
elif dependency in manifest.metrics:
|
elif dependency in manifest.metrics:
|
||||||
linker.dependency(node.unique_id, (manifest.metrics[dependency].unique_id))
|
linker.dependency(node.unique_id, (manifest.metrics[dependency].unique_id))
|
||||||
else:
|
else:
|
||||||
dependency_not_found(node, dependency)
|
raise GraphDependencyNotFound(node, dependency)
|
||||||
|
|
||||||
def link_graph(self, linker: Linker, manifest: Manifest, add_test_edges: bool = False):
|
def link_graph(self, linker: Linker, manifest: Manifest, add_test_edges: bool = False):
|
||||||
for source in manifest.sources.values():
|
for source in manifest.sources.values():
|
||||||
@@ -508,10 +487,13 @@ class Compiler:
|
|||||||
return Graph(linker.graph)
|
return Graph(linker.graph)
|
||||||
|
|
||||||
# writes the "compiled_code" into the target/compiled directory
|
# writes the "compiled_code" into the target/compiled directory
|
||||||
def _write_node(self, node: NonSourceCompiledNode) -> ManifestNode:
|
def _write_node(self, node: ManifestSQLNode) -> ManifestSQLNode:
|
||||||
if not node.extra_ctes_injected or node.resource_type == NodeType.Snapshot:
|
if not node.extra_ctes_injected or node.resource_type in (
|
||||||
|
NodeType.Snapshot,
|
||||||
|
NodeType.Seed,
|
||||||
|
):
|
||||||
return node
|
return node
|
||||||
fire_event(WritingInjectedSQLForNode(unique_id=node.unique_id))
|
fire_event(WritingInjectedSQLForNode(node_info=get_node_info()))
|
||||||
|
|
||||||
if node.compiled_code:
|
if node.compiled_code:
|
||||||
node.compiled_path = node.write_node(
|
node.compiled_path = node.write_node(
|
||||||
@@ -521,11 +503,11 @@ class Compiler:
|
|||||||
|
|
||||||
def compile_node(
|
def compile_node(
|
||||||
self,
|
self,
|
||||||
node: ManifestNode,
|
node: ManifestSQLNode,
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
extra_context: Optional[Dict[str, Any]] = None,
|
extra_context: Optional[Dict[str, Any]] = None,
|
||||||
write: bool = True,
|
write: bool = True,
|
||||||
) -> NonSourceCompiledNode:
|
) -> ManifestSQLNode:
|
||||||
"""This is the main entry point into this code. It's called by
|
"""This is the main entry point into this code. It's called by
|
||||||
CompileRunner.compile, GenericRPCRunner.compile, and
|
CompileRunner.compile, GenericRPCRunner.compile, and
|
||||||
RunTask.get_hook_sql. It calls '_compile_node' to convert
|
RunTask.get_hook_sql. It calls '_compile_node' to convert
|
||||||
|
|||||||
@@ -9,12 +9,14 @@ from dbt.clients.system import load_file_contents
|
|||||||
from dbt.clients.yaml_helper import load_yaml_text
|
from dbt.clients.yaml_helper import load_yaml_text
|
||||||
from dbt.contracts.connection import Credentials, HasCredentials
|
from dbt.contracts.connection import Credentials, HasCredentials
|
||||||
from dbt.contracts.project import ProfileConfig, UserConfig
|
from dbt.contracts.project import ProfileConfig, UserConfig
|
||||||
from dbt.exceptions import CompilationException
|
from dbt.exceptions import (
|
||||||
from dbt.exceptions import DbtProfileError
|
CompilationException,
|
||||||
from dbt.exceptions import DbtProjectError
|
DbtProfileError,
|
||||||
from dbt.exceptions import ValidationException
|
DbtProjectError,
|
||||||
from dbt.exceptions import RuntimeException
|
ValidationException,
|
||||||
from dbt.exceptions import validator_error_message
|
RuntimeException,
|
||||||
|
ProfileConfigInvalid,
|
||||||
|
)
|
||||||
from dbt.events.types import MissingProfileTarget
|
from dbt.events.types import MissingProfileTarget
|
||||||
from dbt.events.functions import fire_event
|
from dbt.events.functions import fire_event
|
||||||
from dbt.utils import coerce_dict_str
|
from dbt.utils import coerce_dict_str
|
||||||
@@ -156,7 +158,7 @@ class Profile(HasCredentials):
|
|||||||
dct = self.to_profile_info(serialize_credentials=True)
|
dct = self.to_profile_info(serialize_credentials=True)
|
||||||
ProfileConfig.validate(dct)
|
ProfileConfig.validate(dct)
|
||||||
except ValidationError as exc:
|
except ValidationError as exc:
|
||||||
raise DbtProfileError(validator_error_message(exc)) from exc
|
raise ProfileConfigInvalid(exc) from exc
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _credentials_from_profile(
|
def _credentials_from_profile(
|
||||||
|
|||||||
@@ -16,19 +16,19 @@ import hashlib
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from dbt import flags, deprecations
|
from dbt import flags, deprecations
|
||||||
from dbt.clients.system import resolve_path_from_base
|
from dbt.clients.system import path_exists, resolve_path_from_base, load_file_contents
|
||||||
from dbt.clients.system import path_exists
|
|
||||||
from dbt.clients.system import load_file_contents
|
|
||||||
from dbt.clients.yaml_helper import load_yaml_text
|
from dbt.clients.yaml_helper import load_yaml_text
|
||||||
from dbt.contracts.connection import QueryComment
|
from dbt.contracts.connection import QueryComment
|
||||||
from dbt.exceptions import DbtProjectError
|
from dbt.exceptions import (
|
||||||
from dbt.exceptions import SemverException
|
DbtProjectError,
|
||||||
from dbt.exceptions import validator_error_message
|
SemverException,
|
||||||
from dbt.exceptions import RuntimeException
|
ProjectContractBroken,
|
||||||
|
ProjectContractInvalid,
|
||||||
|
RuntimeException,
|
||||||
|
)
|
||||||
from dbt.graph import SelectionSpec
|
from dbt.graph import SelectionSpec
|
||||||
from dbt.helper_types import NoValue
|
from dbt.helper_types import NoValue
|
||||||
from dbt.semver import VersionSpecifier
|
from dbt.semver import VersionSpecifier, versions_compatible
|
||||||
from dbt.semver import versions_compatible
|
|
||||||
from dbt.version import get_installed_version
|
from dbt.version import get_installed_version
|
||||||
from dbt.utils import MultiDict
|
from dbt.utils import MultiDict
|
||||||
from dbt.node_types import NodeType
|
from dbt.node_types import NodeType
|
||||||
@@ -293,7 +293,7 @@ class PartialProject(RenderComponents):
|
|||||||
packages_data = renderer.render_data(self.packages_dict)
|
packages_data = renderer.render_data(self.packages_dict)
|
||||||
packages_config = package_config_from_data(packages_data)
|
packages_config = package_config_from_data(packages_data)
|
||||||
if not self.project_name:
|
if not self.project_name:
|
||||||
raise DbtProjectError(DbtProjectError("Package dbt_project.yml must have a name!"))
|
raise DbtProjectError("Package dbt_project.yml must have a name!")
|
||||||
return ProjectPackageMetadata(self.project_name, packages_config.packages)
|
return ProjectPackageMetadata(self.project_name, packages_config.packages)
|
||||||
|
|
||||||
def check_config_path(self, project_dict, deprecated_path, exp_path):
|
def check_config_path(self, project_dict, deprecated_path, exp_path):
|
||||||
@@ -332,7 +332,7 @@ class PartialProject(RenderComponents):
|
|||||||
ProjectContract.validate(rendered.project_dict)
|
ProjectContract.validate(rendered.project_dict)
|
||||||
cfg = ProjectContract.from_dict(rendered.project_dict)
|
cfg = ProjectContract.from_dict(rendered.project_dict)
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
raise DbtProjectError(validator_error_message(e)) from e
|
raise ProjectContractInvalid(e) from e
|
||||||
# name/version are required in the Project definition, so we can assume
|
# name/version are required in the Project definition, so we can assume
|
||||||
# they are present
|
# they are present
|
||||||
name = cfg.name
|
name = cfg.name
|
||||||
@@ -649,7 +649,7 @@ class Project:
|
|||||||
try:
|
try:
|
||||||
ProjectContract.validate(self.to_project_config())
|
ProjectContract.validate(self.to_project_config())
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
raise DbtProjectError(validator_error_message(e)) from e
|
raise ProjectContractBroken(e) from e
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_project_root(
|
def from_project_root(
|
||||||
|
|||||||
@@ -3,31 +3,59 @@ import os
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, Optional, Mapping, Iterator, Iterable, Tuple, List, MutableSet, Type
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Dict,
|
||||||
|
Iterable,
|
||||||
|
Iterator,
|
||||||
|
Mapping,
|
||||||
|
MutableSet,
|
||||||
|
Optional,
|
||||||
|
Tuple,
|
||||||
|
Type,
|
||||||
|
)
|
||||||
|
|
||||||
from .profile import Profile
|
|
||||||
from .project import Project
|
|
||||||
from .renderer import DbtProjectYamlRenderer, ProfileRenderer
|
|
||||||
from dbt import flags
|
from dbt import flags
|
||||||
from dbt.adapters.factory import get_relation_class_by_name, get_include_paths
|
from dbt.adapters.factory import get_include_paths, get_relation_class_by_name
|
||||||
from dbt.helper_types import FQNPath, PathSet, DictDefaultEmptyStr
|
|
||||||
from dbt.config.profile import read_user_config
|
from dbt.config.profile import read_user_config
|
||||||
from dbt.config.project import load_raw_project
|
from dbt.config.project import load_raw_project
|
||||||
from dbt.contracts.connection import AdapterRequiredConfig, Credentials, HasCredentials
|
from dbt.contracts.connection import AdapterRequiredConfig, Credentials, HasCredentials
|
||||||
from dbt.contracts.graph.manifest import ManifestMetadata
|
from dbt.contracts.graph.manifest import ManifestMetadata
|
||||||
from dbt.contracts.relation import ComponentName
|
|
||||||
from dbt.ui import warning_tag
|
|
||||||
|
|
||||||
from dbt.contracts.project import Configuration, UserConfig
|
from dbt.contracts.project import Configuration, UserConfig
|
||||||
from dbt.exceptions import (
|
from dbt.contracts.relation import ComponentName
|
||||||
RuntimeException,
|
|
||||||
DbtProjectError,
|
|
||||||
validator_error_message,
|
|
||||||
warn_or_error,
|
|
||||||
raise_compiler_error,
|
|
||||||
)
|
|
||||||
|
|
||||||
from dbt.dataclass_schema import ValidationError
|
from dbt.dataclass_schema import ValidationError
|
||||||
|
from dbt.exceptions import (
|
||||||
|
ConfigContractBroken,
|
||||||
|
DbtProjectError,
|
||||||
|
NonUniquePackageName,
|
||||||
|
RuntimeException,
|
||||||
|
UninstalledPackagesFound,
|
||||||
|
)
|
||||||
|
from dbt.events.functions import warn_or_error
|
||||||
|
from dbt.events.types import UnusedResourceConfigPath
|
||||||
|
from dbt.helper_types import DictDefaultEmptyStr, FQNPath, PathSet
|
||||||
|
|
||||||
|
from .profile import Profile
|
||||||
|
from .project import Project
|
||||||
|
from .renderer import DbtProjectYamlRenderer, ProfileRenderer
|
||||||
|
|
||||||
|
from dbt.config.utils import parse_cli_vars
|
||||||
|
|
||||||
|
|
||||||
|
def get_project_and_cli_vars_from_args(args, project_dir: str) -> Tuple[Project, Dict[str, Any]]:
|
||||||
|
profile = UnsetProfile()
|
||||||
|
# The profile (for warehouse connection) is not needed, but we want
|
||||||
|
# to get the UserConfig, which is also in profiles.yml
|
||||||
|
user_config = read_user_config(flags.PROFILES_DIR)
|
||||||
|
profile.user_config = user_config
|
||||||
|
|
||||||
|
# parse_cli_vars is embedded into the param when using click.
|
||||||
|
# replace this with cli_vars: Dict[str, Any] = getattr(args, "vars", {})
|
||||||
|
# when tasks are refactored for click
|
||||||
|
cli_vars: Dict[str, Any] = parse_cli_vars(getattr(args, "vars", "{}"))
|
||||||
|
project_root: str = args.project_dir or project_dir
|
||||||
|
project = load_project(project_root, args.version_check, profile, cli_vars)
|
||||||
|
return project, cli_vars
|
||||||
|
|
||||||
|
|
||||||
def load_project(
|
def load_project(
|
||||||
@@ -97,9 +125,9 @@ class RuntimeConfig(Project, Profile, AdapterRequiredConfig):
|
|||||||
return load_profile(
|
return load_profile(
|
||||||
project_root,
|
project_root,
|
||||||
cli_vars,
|
cli_vars,
|
||||||
args.profile,
|
getattr(args, "profile", None),
|
||||||
args.target,
|
getattr(args, "target", None),
|
||||||
args.threads,
|
getattr(args, "threads", None),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Called by 'new_project' and 'from_args'
|
# Called by 'new_project' and 'from_args'
|
||||||
@@ -124,7 +152,10 @@ class RuntimeConfig(Project, Profile, AdapterRequiredConfig):
|
|||||||
.replace_dict(_project_quoting_dict(project, profile))
|
.replace_dict(_project_quoting_dict(project, profile))
|
||||||
).to_dict(omit_none=True)
|
).to_dict(omit_none=True)
|
||||||
|
|
||||||
cli_vars: Dict[str, Any] = getattr(args, "vars", {})
|
cli_vars = getattr(args, "vars", "{}")
|
||||||
|
cli_vars: Dict[str, Any] = (
|
||||||
|
parse_cli_vars(cli_vars) if isinstance(cli_vars, str) else cli_vars
|
||||||
|
)
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
project_name=project.project_name,
|
project_name=project.project_name,
|
||||||
@@ -227,20 +258,23 @@ class RuntimeConfig(Project, Profile, AdapterRequiredConfig):
|
|||||||
try:
|
try:
|
||||||
Configuration.validate(self.serialize())
|
Configuration.validate(self.serialize())
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
raise DbtProjectError(validator_error_message(e)) from e
|
raise ConfigContractBroken(e) from e
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def collect_parts(cls: Type["RuntimeConfig"], args: Any) -> Tuple[Project, Profile]:
|
def collect_parts(cls: Type["RuntimeConfig"], args: Any) -> Tuple[Project, Profile]:
|
||||||
# profile_name from the project
|
# profile_name from the project
|
||||||
project_root = args.project_dir if args.project_dir else os.getcwd()
|
project_root = args.project_dir if args.project_dir else os.getcwd()
|
||||||
cli_vars: Dict[str, Any] = getattr(args, "vars", {})
|
cli_vars = getattr(args, "vars", "{}")
|
||||||
|
cli_vars: Dict[str, Any] = (
|
||||||
|
parse_cli_vars(cli_vars) if isinstance(cli_vars, str) else cli_vars
|
||||||
|
)
|
||||||
profile = cls.get_profile(
|
profile = cls.get_profile(
|
||||||
project_root,
|
project_root,
|
||||||
cli_vars,
|
cli_vars,
|
||||||
args,
|
args,
|
||||||
)
|
)
|
||||||
project = load_project(project_root, bool(flags.VERSION_CHECK), profile, cli_vars)
|
project = load_project(project_root, bool(flags.VERSION_CHECK), profile, cli_vars)
|
||||||
return (project, profile)
|
return project, profile
|
||||||
|
|
||||||
# Called in main.py, lib.py, task/base.py
|
# Called in main.py, lib.py, task/base.py
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -309,11 +343,11 @@ class RuntimeConfig(Project, Profile, AdapterRequiredConfig):
|
|||||||
"exposures": self._get_config_paths(self.exposures),
|
"exposures": self._get_config_paths(self.exposures),
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_unused_resource_config_paths(
|
def warn_for_unused_resource_config_paths(
|
||||||
self,
|
self,
|
||||||
resource_fqns: Mapping[str, PathSet],
|
resource_fqns: Mapping[str, PathSet],
|
||||||
disabled: PathSet,
|
disabled: PathSet,
|
||||||
) -> List[FQNPath]:
|
) -> None:
|
||||||
"""Return a list of lists of strings, where each inner list of strings
|
"""Return a list of lists of strings, where each inner list of strings
|
||||||
represents a type + FQN path of a resource configuration that is not
|
represents a type + FQN path of a resource configuration that is not
|
||||||
used.
|
used.
|
||||||
@@ -327,23 +361,13 @@ class RuntimeConfig(Project, Profile, AdapterRequiredConfig):
|
|||||||
|
|
||||||
for config_path in config_paths:
|
for config_path in config_paths:
|
||||||
if not _is_config_used(config_path, fqns):
|
if not _is_config_used(config_path, fqns):
|
||||||
unused_resource_config_paths.append((resource_type,) + config_path)
|
resource_path = ".".join(i for i in ((resource_type,) + config_path))
|
||||||
return unused_resource_config_paths
|
unused_resource_config_paths.append(resource_path)
|
||||||
|
|
||||||
def warn_for_unused_resource_config_paths(
|
if len(unused_resource_config_paths) == 0:
|
||||||
self,
|
|
||||||
resource_fqns: Mapping[str, PathSet],
|
|
||||||
disabled: PathSet,
|
|
||||||
) -> None:
|
|
||||||
unused = self.get_unused_resource_config_paths(resource_fqns, disabled)
|
|
||||||
if len(unused) == 0:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
msg = UNUSED_RESOURCE_CONFIGURATION_PATH_MESSAGE.format(
|
warn_or_error(UnusedResourceConfigPath(unused_config_paths=unused_resource_config_paths))
|
||||||
len(unused), "\n".join("- {}".format(".".join(u)) for u in unused)
|
|
||||||
)
|
|
||||||
|
|
||||||
warn_or_error(msg, log_fmt=warning_tag("{}"))
|
|
||||||
|
|
||||||
def load_dependencies(self, base_only=False) -> Mapping[str, "RuntimeConfig"]:
|
def load_dependencies(self, base_only=False) -> Mapping[str, "RuntimeConfig"]:
|
||||||
if self.dependencies is None:
|
if self.dependencies is None:
|
||||||
@@ -357,22 +381,15 @@ class RuntimeConfig(Project, Profile, AdapterRequiredConfig):
|
|||||||
count_packages_specified = len(self.packages.packages) # type: ignore
|
count_packages_specified = len(self.packages.packages) # type: ignore
|
||||||
count_packages_installed = len(tuple(self._get_project_directories()))
|
count_packages_installed = len(tuple(self._get_project_directories()))
|
||||||
if count_packages_specified > count_packages_installed:
|
if count_packages_specified > count_packages_installed:
|
||||||
raise_compiler_error(
|
raise UninstalledPackagesFound(
|
||||||
f"dbt found {count_packages_specified} package(s) "
|
count_packages_specified,
|
||||||
f"specified in packages.yml, but only "
|
count_packages_installed,
|
||||||
f"{count_packages_installed} package(s) installed "
|
self.packages_install_path,
|
||||||
f'in {self.packages_install_path}. Run "dbt deps" to '
|
|
||||||
f"install package dependencies."
|
|
||||||
)
|
)
|
||||||
project_paths = itertools.chain(internal_packages, self._get_project_directories())
|
project_paths = itertools.chain(internal_packages, self._get_project_directories())
|
||||||
for project_name, project in self.load_projects(project_paths):
|
for project_name, project in self.load_projects(project_paths):
|
||||||
if project_name in all_projects:
|
if project_name in all_projects:
|
||||||
raise_compiler_error(
|
raise NonUniquePackageName(project_name)
|
||||||
f"dbt found more than one package with the name "
|
|
||||||
f'"{project_name}" included in this project. Package '
|
|
||||||
f"names must be unique in a project. Please rename "
|
|
||||||
f"one of these packages."
|
|
||||||
)
|
|
||||||
all_projects[project_name] = project
|
all_projects[project_name] = project
|
||||||
self.dependencies = all_projects
|
self.dependencies = all_projects
|
||||||
return self.dependencies
|
return self.dependencies
|
||||||
@@ -627,14 +644,6 @@ class UnsetProfileConfig(RuntimeConfig):
|
|||||||
return cls.from_parts(project=project, profile=profile, args=args)
|
return cls.from_parts(project=project, profile=profile, args=args)
|
||||||
|
|
||||||
|
|
||||||
UNUSED_RESOURCE_CONFIGURATION_PATH_MESSAGE = """\
|
|
||||||
Configuration paths exist in your dbt_project.yml file which do not \
|
|
||||||
apply to any resources.
|
|
||||||
There are {} unused configuration paths:
|
|
||||||
{}
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def _is_config_used(path, fqns):
|
def _is_config_used(path, fqns):
|
||||||
if fqns:
|
if fqns:
|
||||||
for fqn in fqns:
|
for fqn in fqns:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from dbt.config import Profile, Project, read_user_config
|
|||||||
from dbt.config.renderer import DbtProjectYamlRenderer, ProfileRenderer
|
from dbt.config.renderer import DbtProjectYamlRenderer, ProfileRenderer
|
||||||
from dbt.events.functions import fire_event
|
from dbt.events.functions import fire_event
|
||||||
from dbt.events.types import InvalidVarsYAML
|
from dbt.events.types import InvalidVarsYAML
|
||||||
from dbt.exceptions import ValidationException, raise_compiler_error
|
from dbt.exceptions import ValidationException, VarsArgNotYamlDict
|
||||||
|
|
||||||
|
|
||||||
def parse_cli_vars(var: str) -> Dict[str, Any]:
|
def parse_cli_vars(var: str) -> Dict[str, Any]:
|
||||||
@@ -19,11 +19,7 @@ def parse_cli_vars(var: str) -> Dict[str, Any]:
|
|||||||
if var_type is dict:
|
if var_type is dict:
|
||||||
return cli_vars
|
return cli_vars
|
||||||
else:
|
else:
|
||||||
type_name = var_type.__name__
|
raise VarsArgNotYamlDict(var_type)
|
||||||
raise_compiler_error(
|
|
||||||
"The --vars argument must be a YAML dictionary, but was "
|
|
||||||
"of type '{}'".format(type_name)
|
|
||||||
)
|
|
||||||
except ValidationException:
|
except ValidationException:
|
||||||
fire_event(InvalidVarsYAML())
|
fire_event(InvalidVarsYAML())
|
||||||
raise
|
raise
|
||||||
@@ -67,9 +63,9 @@ def get_project_config(
|
|||||||
profile = Profile.render(
|
profile = Profile.render(
|
||||||
ProfileRenderer(cli_vars),
|
ProfileRenderer(cli_vars),
|
||||||
profile_name,
|
profile_name,
|
||||||
args.THREADS,
|
getattr(args, "threads", None),
|
||||||
args.TARGET,
|
getattr(args, "target", None),
|
||||||
args.PROFILE,
|
getattr(args, "profile", None),
|
||||||
)
|
)
|
||||||
# Generate a project
|
# Generate a project
|
||||||
project = Project.from_project_root(
|
project = Project.from_project_root(
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
SECRET_ENV_PREFIX = "DBT_ENV_SECRET_"
|
SECRET_ENV_PREFIX = "DBT_ENV_SECRET_"
|
||||||
DEFAULT_ENV_PLACEHOLDER = "DBT_DEFAULT_PLACEHOLDER"
|
DEFAULT_ENV_PLACEHOLDER = "DBT_DEFAULT_PLACEHOLDER"
|
||||||
METADATA_ENV_PREFIX = "DBT_ENV_CUSTOM_ENV_"
|
METADATA_ENV_PREFIX = "DBT_ENV_CUSTOM_ENV_"
|
||||||
|
|
||||||
|
MAXIMUM_SEED_SIZE = 1 * 1024 * 1024
|
||||||
|
MAXIMUM_SEED_SIZE_NAME = "1MB"
|
||||||
|
|
||||||
|
PIN_PACKAGE_URL = (
|
||||||
|
"https://docs.getdbt.com/docs/package-management#section-specifying-package-versions"
|
||||||
|
)
|
||||||
|
|||||||
@@ -4,19 +4,22 @@ from typing import Any, Dict, NoReturn, Optional, Mapping, Iterable, Set, List
|
|||||||
|
|
||||||
from dbt import flags
|
from dbt import flags
|
||||||
from dbt import tracking
|
from dbt import tracking
|
||||||
|
from dbt import utils
|
||||||
from dbt.clients.jinja import get_rendered
|
from dbt.clients.jinja import get_rendered
|
||||||
from dbt.clients.yaml_helper import yaml, safe_load, SafeLoader, Loader, Dumper # noqa: F401
|
from dbt.clients.yaml_helper import yaml, safe_load, SafeLoader, Loader, Dumper # noqa: F401
|
||||||
from dbt.constants import SECRET_ENV_PREFIX, DEFAULT_ENV_PLACEHOLDER
|
from dbt.constants import SECRET_ENV_PREFIX, DEFAULT_ENV_PLACEHOLDER
|
||||||
from dbt.contracts.graph.compiled import CompiledResource
|
from dbt.contracts.graph.nodes import Resource
|
||||||
from dbt.exceptions import (
|
from dbt.exceptions import (
|
||||||
CompilationException,
|
DisallowSecretEnvVar,
|
||||||
|
EnvVarMissing,
|
||||||
MacroReturn,
|
MacroReturn,
|
||||||
raise_compiler_error,
|
RequiredVarNotFound,
|
||||||
raise_parsing_error,
|
SetStrictWrongType,
|
||||||
disallow_secret_env_var,
|
ZipStrictWrongType,
|
||||||
)
|
)
|
||||||
from dbt.events.functions import fire_event, get_invocation_id
|
from dbt.events.functions import fire_event, get_invocation_id
|
||||||
from dbt.events.types import MacroEventInfo, MacroEventDebug
|
from dbt.events.types import JinjaLogInfo, JinjaLogDebug
|
||||||
|
from dbt.events.contextvars import get_node_info
|
||||||
from dbt.version import __version__ as dbt_version
|
from dbt.version import __version__ as dbt_version
|
||||||
|
|
||||||
# These modules are added to the context. Consider alternative
|
# These modules are added to the context. Consider alternative
|
||||||
@@ -126,18 +129,17 @@ class ContextMeta(type):
|
|||||||
|
|
||||||
|
|
||||||
class Var:
|
class Var:
|
||||||
UndefinedVarError = "Required var '{}' not found in config:\nVars supplied to {} = {}"
|
|
||||||
_VAR_NOTSET = object()
|
_VAR_NOTSET = object()
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
context: Mapping[str, Any],
|
context: Mapping[str, Any],
|
||||||
cli_vars: Mapping[str, Any],
|
cli_vars: Mapping[str, Any],
|
||||||
node: Optional[CompiledResource] = None,
|
node: Optional[Resource] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self._context: Mapping[str, Any] = context
|
self._context: Mapping[str, Any] = context
|
||||||
self._cli_vars: Mapping[str, Any] = cli_vars
|
self._cli_vars: Mapping[str, Any] = cli_vars
|
||||||
self._node: Optional[CompiledResource] = node
|
self._node: Optional[Resource] = node
|
||||||
self._merged: Mapping[str, Any] = self._generate_merged()
|
self._merged: Mapping[str, Any] = self._generate_merged()
|
||||||
|
|
||||||
def _generate_merged(self) -> Mapping[str, Any]:
|
def _generate_merged(self) -> Mapping[str, Any]:
|
||||||
@@ -151,10 +153,7 @@ class Var:
|
|||||||
return "<Configuration>"
|
return "<Configuration>"
|
||||||
|
|
||||||
def get_missing_var(self, var_name):
|
def get_missing_var(self, var_name):
|
||||||
dct = {k: self._merged[k] for k in self._merged}
|
raise RequiredVarNotFound(var_name, self._merged, self._node)
|
||||||
pretty_vars = json.dumps(dct, sort_keys=True, indent=4)
|
|
||||||
msg = self.UndefinedVarError.format(var_name, self.node_name, pretty_vars)
|
|
||||||
raise_compiler_error(msg, self._node)
|
|
||||||
|
|
||||||
def has_var(self, var_name: str):
|
def has_var(self, var_name: str):
|
||||||
return var_name in self._merged
|
return var_name in self._merged
|
||||||
@@ -298,7 +297,7 @@ class BaseContext(metaclass=ContextMeta):
|
|||||||
"""
|
"""
|
||||||
return_value = None
|
return_value = None
|
||||||
if var.startswith(SECRET_ENV_PREFIX):
|
if var.startswith(SECRET_ENV_PREFIX):
|
||||||
disallow_secret_env_var(var)
|
raise DisallowSecretEnvVar(var)
|
||||||
if var in os.environ:
|
if var in os.environ:
|
||||||
return_value = os.environ[var]
|
return_value = os.environ[var]
|
||||||
elif default is not None:
|
elif default is not None:
|
||||||
@@ -313,8 +312,7 @@ class BaseContext(metaclass=ContextMeta):
|
|||||||
|
|
||||||
return return_value
|
return return_value
|
||||||
else:
|
else:
|
||||||
msg = f"Env var required but not provided: '{var}'"
|
raise EnvVarMissing(var)
|
||||||
raise_parsing_error(msg)
|
|
||||||
|
|
||||||
if os.environ.get("DBT_MACRO_DEBUGGING"):
|
if os.environ.get("DBT_MACRO_DEBUGGING"):
|
||||||
|
|
||||||
@@ -495,7 +493,7 @@ class BaseContext(metaclass=ContextMeta):
|
|||||||
try:
|
try:
|
||||||
return set(value)
|
return set(value)
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
raise CompilationException(e)
|
raise SetStrictWrongType(e)
|
||||||
|
|
||||||
@contextmember("zip")
|
@contextmember("zip")
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -539,7 +537,7 @@ class BaseContext(metaclass=ContextMeta):
|
|||||||
try:
|
try:
|
||||||
return zip(*args)
|
return zip(*args)
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
raise CompilationException(e)
|
raise ZipStrictWrongType(e)
|
||||||
|
|
||||||
@contextmember
|
@contextmember
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -557,9 +555,9 @@ class BaseContext(metaclass=ContextMeta):
|
|||||||
{% endmacro %}"
|
{% endmacro %}"
|
||||||
"""
|
"""
|
||||||
if info:
|
if info:
|
||||||
fire_event(MacroEventInfo(msg=msg))
|
fire_event(JinjaLogInfo(msg=msg, node_info=get_node_info()))
|
||||||
else:
|
else:
|
||||||
fire_event(MacroEventDebug(msg=msg))
|
fire_event(JinjaLogDebug(msg=msg, node_info=get_node_info()))
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@contextproperty
|
@contextproperty
|
||||||
@@ -687,6 +685,19 @@ class BaseContext(metaclass=ContextMeta):
|
|||||||
dict_diff.update({k: dict_a[k]})
|
dict_diff.update({k: dict_a[k]})
|
||||||
return dict_diff
|
return dict_diff
|
||||||
|
|
||||||
|
@contextmember
|
||||||
|
@staticmethod
|
||||||
|
def local_md5(value: str) -> str:
|
||||||
|
"""Calculates an MD5 hash of the given string.
|
||||||
|
It's called "local_md5" to emphasize that it runs locally in dbt (in jinja context) and not an MD5 SQL command.
|
||||||
|
|
||||||
|
:param value: The value to hash
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
{% set value_hash = local_md5("hello world") %}
|
||||||
|
"""
|
||||||
|
return utils.md5(value)
|
||||||
|
|
||||||
|
|
||||||
def generate_base_context(cli_vars: Dict[str, Any]) -> Dict[str, Any]:
|
def generate_base_context(cli_vars: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
ctx = BaseContext(cli_vars)
|
ctx = BaseContext(cli_vars)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from dbt.utils import MultiDict
|
|||||||
|
|
||||||
from dbt.context.base import contextproperty, contextmember, Var
|
from dbt.context.base import contextproperty, contextmember, Var
|
||||||
from dbt.context.target import TargetContext
|
from dbt.context.target import TargetContext
|
||||||
from dbt.exceptions import raise_parsing_error, disallow_secret_env_var
|
from dbt.exceptions import EnvVarMissing, DisallowSecretEnvVar
|
||||||
|
|
||||||
|
|
||||||
class ConfiguredContext(TargetContext):
|
class ConfiguredContext(TargetContext):
|
||||||
@@ -87,7 +87,7 @@ class SchemaYamlContext(ConfiguredContext):
|
|||||||
def env_var(self, var: str, default: Optional[str] = None) -> str:
|
def env_var(self, var: str, default: Optional[str] = None) -> str:
|
||||||
return_value = None
|
return_value = None
|
||||||
if var.startswith(SECRET_ENV_PREFIX):
|
if var.startswith(SECRET_ENV_PREFIX):
|
||||||
disallow_secret_env_var(var)
|
raise DisallowSecretEnvVar(var)
|
||||||
if var in os.environ:
|
if var in os.environ:
|
||||||
return_value = os.environ[var]
|
return_value = os.environ[var]
|
||||||
elif default is not None:
|
elif default is not None:
|
||||||
@@ -105,8 +105,7 @@ class SchemaYamlContext(ConfiguredContext):
|
|||||||
|
|
||||||
return return_value
|
return return_value
|
||||||
else:
|
else:
|
||||||
msg = f"Env var required but not provided: '{var}'"
|
raise EnvVarMissing(var)
|
||||||
raise_parsing_error(msg)
|
|
||||||
|
|
||||||
|
|
||||||
class MacroResolvingContext(ConfiguredContext):
|
class MacroResolvingContext(ConfiguredContext):
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
from typing import Any, Dict, Union
|
from typing import Any, Dict, Union
|
||||||
|
|
||||||
from dbt.exceptions import (
|
from dbt.exceptions import (
|
||||||
doc_invalid_args,
|
DocTargetNotFound,
|
||||||
doc_target_not_found,
|
InvalidDocArgs,
|
||||||
)
|
)
|
||||||
from dbt.config.runtime import RuntimeConfig
|
from dbt.config.runtime import RuntimeConfig
|
||||||
from dbt.contracts.graph.compiled import CompileResultNode
|
|
||||||
from dbt.contracts.graph.manifest import Manifest
|
from dbt.contracts.graph.manifest import Manifest
|
||||||
from dbt.contracts.graph.parsed import ParsedMacro
|
from dbt.contracts.graph.nodes import Macro, ResultNode
|
||||||
|
|
||||||
from dbt.context.base import contextmember
|
from dbt.context.base import contextmember
|
||||||
from dbt.context.configured import SchemaYamlContext
|
from dbt.context.configured import SchemaYamlContext
|
||||||
@@ -17,7 +16,7 @@ class DocsRuntimeContext(SchemaYamlContext):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
config: RuntimeConfig,
|
config: RuntimeConfig,
|
||||||
node: Union[ParsedMacro, CompileResultNode],
|
node: Union[Macro, ResultNode],
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
current_project: str,
|
current_project: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -53,9 +52,9 @@ class DocsRuntimeContext(SchemaYamlContext):
|
|||||||
elif len(args) == 2:
|
elif len(args) == 2:
|
||||||
doc_package_name, doc_name = args
|
doc_package_name, doc_name = args
|
||||||
else:
|
else:
|
||||||
doc_invalid_args(self.node, args)
|
raise InvalidDocArgs(self.node, args)
|
||||||
|
|
||||||
# ParsedDocumentation
|
# Documentation
|
||||||
target_doc = self.manifest.resolve_doc(
|
target_doc = self.manifest.resolve_doc(
|
||||||
doc_name,
|
doc_name,
|
||||||
doc_package_name,
|
doc_package_name,
|
||||||
@@ -69,7 +68,9 @@ class DocsRuntimeContext(SchemaYamlContext):
|
|||||||
# TODO CT-211
|
# TODO CT-211
|
||||||
source_file.add_node(self.node.unique_id) # type: ignore[union-attr]
|
source_file.add_node(self.node.unique_id) # type: ignore[union-attr]
|
||||||
else:
|
else:
|
||||||
doc_target_not_found(self.node, doc_name, doc_package_name)
|
raise DocTargetNotFound(
|
||||||
|
node=self.node, target_doc_name=doc_name, target_doc_package=doc_package_name
|
||||||
|
)
|
||||||
|
|
||||||
return target_doc.block_contents
|
return target_doc.block_contents
|
||||||
|
|
||||||
|
|||||||
142
core/dbt/context/exceptions_jinja.py
Normal file
142
core/dbt/context/exceptions_jinja.py
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import functools
|
||||||
|
from typing import NoReturn
|
||||||
|
|
||||||
|
from dbt.events.functions import warn_or_error
|
||||||
|
from dbt.events.helpers import env_secrets, scrub_secrets
|
||||||
|
from dbt.events.types import JinjaLogWarning
|
||||||
|
|
||||||
|
from dbt.exceptions import (
|
||||||
|
RuntimeException,
|
||||||
|
MissingConfig,
|
||||||
|
MissingMaterialization,
|
||||||
|
MissingRelation,
|
||||||
|
AmbiguousAlias,
|
||||||
|
AmbiguousCatalogMatch,
|
||||||
|
CacheInconsistency,
|
||||||
|
DataclassNotDict,
|
||||||
|
CompilationException,
|
||||||
|
DatabaseException,
|
||||||
|
DependencyNotFound,
|
||||||
|
DependencyException,
|
||||||
|
DuplicatePatchPath,
|
||||||
|
DuplicateResourceName,
|
||||||
|
InvalidPropertyYML,
|
||||||
|
NotImplementedException,
|
||||||
|
RelationWrongType,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def warn(msg, node=None):
|
||||||
|
warn_or_error(JinjaLogWarning(msg=msg), node=node)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def missing_config(model, name) -> NoReturn:
|
||||||
|
raise MissingConfig(unique_id=model.unique_id, name=name)
|
||||||
|
|
||||||
|
|
||||||
|
def missing_materialization(model, adapter_type) -> NoReturn:
|
||||||
|
raise MissingMaterialization(model=model, adapter_type=adapter_type)
|
||||||
|
|
||||||
|
|
||||||
|
def missing_relation(relation, model=None) -> NoReturn:
|
||||||
|
raise MissingRelation(relation, model)
|
||||||
|
|
||||||
|
|
||||||
|
def raise_ambiguous_alias(node_1, node_2, duped_name=None) -> NoReturn:
|
||||||
|
raise AmbiguousAlias(node_1, node_2, duped_name)
|
||||||
|
|
||||||
|
|
||||||
|
def raise_ambiguous_catalog_match(unique_id, match_1, match_2) -> NoReturn:
|
||||||
|
raise AmbiguousCatalogMatch(unique_id, match_1, match_2)
|
||||||
|
|
||||||
|
|
||||||
|
def raise_cache_inconsistent(message) -> NoReturn:
|
||||||
|
raise CacheInconsistency(message)
|
||||||
|
|
||||||
|
|
||||||
|
def raise_dataclass_not_dict(obj) -> NoReturn:
|
||||||
|
raise DataclassNotDict(obj)
|
||||||
|
|
||||||
|
|
||||||
|
def raise_compiler_error(msg, node=None) -> NoReturn:
|
||||||
|
raise CompilationException(msg, node)
|
||||||
|
|
||||||
|
|
||||||
|
def raise_database_error(msg, node=None) -> NoReturn:
|
||||||
|
raise DatabaseException(msg, node)
|
||||||
|
|
||||||
|
|
||||||
|
def raise_dep_not_found(node, node_description, required_pkg) -> NoReturn:
|
||||||
|
raise DependencyNotFound(node, node_description, required_pkg)
|
||||||
|
|
||||||
|
|
||||||
|
def raise_dependency_error(msg) -> NoReturn:
|
||||||
|
raise DependencyException(scrub_secrets(msg, env_secrets()))
|
||||||
|
|
||||||
|
|
||||||
|
def raise_duplicate_patch_name(patch_1, existing_patch_path) -> NoReturn:
|
||||||
|
raise DuplicatePatchPath(patch_1, existing_patch_path)
|
||||||
|
|
||||||
|
|
||||||
|
def raise_duplicate_resource_name(node_1, node_2) -> NoReturn:
|
||||||
|
raise DuplicateResourceName(node_1, node_2)
|
||||||
|
|
||||||
|
|
||||||
|
def raise_invalid_property_yml_version(path, issue) -> NoReturn:
|
||||||
|
raise InvalidPropertyYML(path, issue)
|
||||||
|
|
||||||
|
|
||||||
|
def raise_not_implemented(msg) -> NoReturn:
|
||||||
|
raise NotImplementedException(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def relation_wrong_type(relation, expected_type, model=None) -> NoReturn:
|
||||||
|
raise RelationWrongType(relation, expected_type, model)
|
||||||
|
|
||||||
|
|
||||||
|
# Update this when a new function should be added to the
|
||||||
|
# dbt context's `exceptions` key!
|
||||||
|
CONTEXT_EXPORTS = {
|
||||||
|
fn.__name__: fn
|
||||||
|
for fn in [
|
||||||
|
warn,
|
||||||
|
missing_config,
|
||||||
|
missing_materialization,
|
||||||
|
missing_relation,
|
||||||
|
raise_ambiguous_alias,
|
||||||
|
raise_ambiguous_catalog_match,
|
||||||
|
raise_cache_inconsistent,
|
||||||
|
raise_dataclass_not_dict,
|
||||||
|
raise_compiler_error,
|
||||||
|
raise_database_error,
|
||||||
|
raise_dep_not_found,
|
||||||
|
raise_dependency_error,
|
||||||
|
raise_duplicate_patch_name,
|
||||||
|
raise_duplicate_resource_name,
|
||||||
|
raise_invalid_property_yml_version,
|
||||||
|
raise_not_implemented,
|
||||||
|
relation_wrong_type,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# wraps context based exceptions in node info
|
||||||
|
def wrapper(model):
|
||||||
|
def wrap(func):
|
||||||
|
@functools.wraps(func)
|
||||||
|
def inner(*args, **kwargs):
|
||||||
|
try:
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
except RuntimeException as exc:
|
||||||
|
exc.add_node(model)
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
return inner
|
||||||
|
|
||||||
|
return wrap
|
||||||
|
|
||||||
|
|
||||||
|
def wrapped_exports(model):
|
||||||
|
wrap = wrapper(model)
|
||||||
|
return {name: wrap(export) for name, export in CONTEXT_EXPORTS.items()}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
from typing import Dict, MutableMapping, Optional
|
from typing import Dict, MutableMapping, Optional
|
||||||
from dbt.contracts.graph.parsed import ParsedMacro
|
from dbt.contracts.graph.nodes import Macro
|
||||||
from dbt.exceptions import raise_duplicate_macro_name, raise_compiler_error
|
from dbt.exceptions import DuplicateMacroName, PackageNotFoundForMacro
|
||||||
from dbt.include.global_project import PROJECT_NAME as GLOBAL_PROJECT_NAME
|
from dbt.include.global_project import PROJECT_NAME as GLOBAL_PROJECT_NAME
|
||||||
from dbt.clients.jinja import MacroGenerator
|
from dbt.clients.jinja import MacroGenerator
|
||||||
|
|
||||||
MacroNamespace = Dict[str, ParsedMacro]
|
MacroNamespace = Dict[str, Macro]
|
||||||
|
|
||||||
|
|
||||||
# This class builds the MacroResolver by adding macros
|
# This class builds the MacroResolver by adding macros
|
||||||
@@ -21,7 +21,7 @@ MacroNamespace = Dict[str, ParsedMacro]
|
|||||||
class MacroResolver:
|
class MacroResolver:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
macros: MutableMapping[str, ParsedMacro],
|
macros: MutableMapping[str, Macro],
|
||||||
root_project_name: str,
|
root_project_name: str,
|
||||||
internal_package_names,
|
internal_package_names,
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -77,7 +77,7 @@ class MacroResolver:
|
|||||||
def _add_macro_to(
|
def _add_macro_to(
|
||||||
self,
|
self,
|
||||||
package_namespaces: Dict[str, MacroNamespace],
|
package_namespaces: Dict[str, MacroNamespace],
|
||||||
macro: ParsedMacro,
|
macro: Macro,
|
||||||
):
|
):
|
||||||
if macro.package_name in package_namespaces:
|
if macro.package_name in package_namespaces:
|
||||||
namespace = package_namespaces[macro.package_name]
|
namespace = package_namespaces[macro.package_name]
|
||||||
@@ -86,10 +86,10 @@ class MacroResolver:
|
|||||||
package_namespaces[macro.package_name] = namespace
|
package_namespaces[macro.package_name] = namespace
|
||||||
|
|
||||||
if macro.name in namespace:
|
if macro.name in namespace:
|
||||||
raise_duplicate_macro_name(macro, macro, macro.package_name)
|
raise DuplicateMacroName(macro, macro, macro.package_name)
|
||||||
package_namespaces[macro.package_name][macro.name] = macro
|
package_namespaces[macro.package_name][macro.name] = macro
|
||||||
|
|
||||||
def add_macro(self, macro: ParsedMacro):
|
def add_macro(self, macro: Macro):
|
||||||
macro_name: str = macro.name
|
macro_name: str = macro.name
|
||||||
|
|
||||||
# internal macros (from plugins) will be processed separately from
|
# internal macros (from plugins) will be processed separately from
|
||||||
@@ -187,7 +187,7 @@ class TestMacroNamespace:
|
|||||||
elif package_name in self.macro_resolver.packages:
|
elif package_name in self.macro_resolver.packages:
|
||||||
macro = self.macro_resolver.packages[package_name].get(name)
|
macro = self.macro_resolver.packages[package_name].get(name)
|
||||||
else:
|
else:
|
||||||
raise_compiler_error(f"Could not find package '{package_name}'")
|
raise PackageNotFoundForMacro(package_name)
|
||||||
if not macro:
|
if not macro:
|
||||||
return None
|
return None
|
||||||
macro_func = MacroGenerator(macro, self.ctx, self.node, self.thread_ctx)
|
macro_func = MacroGenerator(macro, self.ctx, self.node, self.thread_ctx)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
from typing import Any, Dict, Iterable, Union, Optional, List, Iterator, Mapping, Set
|
from typing import Any, Dict, Iterable, Union, Optional, List, Iterator, Mapping, Set
|
||||||
|
|
||||||
from dbt.clients.jinja import MacroGenerator, MacroStack
|
from dbt.clients.jinja import MacroGenerator, MacroStack
|
||||||
from dbt.contracts.graph.parsed import ParsedMacro
|
from dbt.contracts.graph.nodes import Macro
|
||||||
from dbt.include.global_project import PROJECT_NAME as GLOBAL_PROJECT_NAME
|
from dbt.include.global_project import PROJECT_NAME as GLOBAL_PROJECT_NAME
|
||||||
from dbt.exceptions import raise_duplicate_macro_name, raise_compiler_error
|
from dbt.exceptions import DuplicateMacroName, PackageNotFoundForMacro
|
||||||
|
|
||||||
|
|
||||||
FlatNamespace = Dict[str, MacroGenerator]
|
FlatNamespace = Dict[str, MacroGenerator]
|
||||||
@@ -75,7 +75,7 @@ class MacroNamespace(Mapping):
|
|||||||
elif package_name in self.packages:
|
elif package_name in self.packages:
|
||||||
return self.packages[package_name].get(name)
|
return self.packages[package_name].get(name)
|
||||||
else:
|
else:
|
||||||
raise_compiler_error(f"Could not find package '{package_name}'")
|
raise PackageNotFoundForMacro(package_name)
|
||||||
|
|
||||||
|
|
||||||
# This class builds the MacroNamespace by adding macros to
|
# This class builds the MacroNamespace by adding macros to
|
||||||
@@ -112,7 +112,7 @@ class MacroNamespaceBuilder:
|
|||||||
def _add_macro_to(
|
def _add_macro_to(
|
||||||
self,
|
self,
|
||||||
hierarchy: Dict[str, FlatNamespace],
|
hierarchy: Dict[str, FlatNamespace],
|
||||||
macro: ParsedMacro,
|
macro: Macro,
|
||||||
macro_func: MacroGenerator,
|
macro_func: MacroGenerator,
|
||||||
):
|
):
|
||||||
if macro.package_name in hierarchy:
|
if macro.package_name in hierarchy:
|
||||||
@@ -122,10 +122,10 @@ class MacroNamespaceBuilder:
|
|||||||
hierarchy[macro.package_name] = namespace
|
hierarchy[macro.package_name] = namespace
|
||||||
|
|
||||||
if macro.name in namespace:
|
if macro.name in namespace:
|
||||||
raise_duplicate_macro_name(macro_func.macro, macro, macro.package_name)
|
raise DuplicateMacroName(macro_func.macro, macro, macro.package_name)
|
||||||
hierarchy[macro.package_name][macro.name] = macro_func
|
hierarchy[macro.package_name][macro.name] = macro_func
|
||||||
|
|
||||||
def add_macro(self, macro: ParsedMacro, ctx: Dict[str, Any]):
|
def add_macro(self, macro: Macro, ctx: Dict[str, Any]):
|
||||||
macro_name: str = macro.name
|
macro_name: str = macro.name
|
||||||
|
|
||||||
# MacroGenerator is in clients/jinja.py
|
# MacroGenerator is in clients/jinja.py
|
||||||
@@ -147,13 +147,11 @@ class MacroNamespaceBuilder:
|
|||||||
elif macro.package_name == self.root_package:
|
elif macro.package_name == self.root_package:
|
||||||
self.globals[macro_name] = macro_func
|
self.globals[macro_name] = macro_func
|
||||||
|
|
||||||
def add_macros(self, macros: Iterable[ParsedMacro], ctx: Dict[str, Any]):
|
def add_macros(self, macros: Iterable[Macro], ctx: Dict[str, Any]):
|
||||||
for macro in macros:
|
for macro in macros:
|
||||||
self.add_macro(macro, ctx)
|
self.add_macro(macro, ctx)
|
||||||
|
|
||||||
def build_namespace(
|
def build_namespace(self, macros: Iterable[Macro], ctx: Dict[str, Any]) -> MacroNamespace:
|
||||||
self, macros: Iterable[ParsedMacro], ctx: Dict[str, Any]
|
|
||||||
) -> MacroNamespace:
|
|
||||||
self.add_macros(macros, ctx)
|
self.add_macros(macros, ctx)
|
||||||
|
|
||||||
# Iterate in reverse-order and overwrite: the packages that are first
|
# Iterate in reverse-order and overwrite: the packages that are first
|
||||||
|
|||||||
@@ -19,46 +19,50 @@ from dbt.adapters.factory import get_adapter, get_adapter_package_names, get_ada
|
|||||||
from dbt.clients import agate_helper
|
from dbt.clients import agate_helper
|
||||||
from dbt.clients.jinja import get_rendered, MacroGenerator, MacroStack
|
from dbt.clients.jinja import get_rendered, MacroGenerator, MacroStack
|
||||||
from dbt.config import RuntimeConfig, Project
|
from dbt.config import RuntimeConfig, Project
|
||||||
from .base import contextmember, contextproperty, Var
|
|
||||||
from .configured import FQNLookup
|
|
||||||
from .context_config import ContextConfig
|
|
||||||
from dbt.constants import SECRET_ENV_PREFIX, DEFAULT_ENV_PLACEHOLDER
|
from dbt.constants import SECRET_ENV_PREFIX, DEFAULT_ENV_PLACEHOLDER
|
||||||
|
from dbt.context.base import contextmember, contextproperty, Var
|
||||||
|
from dbt.context.configured import FQNLookup
|
||||||
|
from dbt.context.context_config import ContextConfig
|
||||||
|
from dbt.context.exceptions_jinja import wrapped_exports
|
||||||
from dbt.context.macro_resolver import MacroResolver, TestMacroNamespace
|
from dbt.context.macro_resolver import MacroResolver, TestMacroNamespace
|
||||||
from .macros import MacroNamespaceBuilder, MacroNamespace
|
from dbt.context.macros import MacroNamespaceBuilder, MacroNamespace
|
||||||
from .manifest import ManifestContext
|
from dbt.context.manifest import ManifestContext
|
||||||
from dbt.contracts.connection import AdapterResponse
|
from dbt.contracts.connection import AdapterResponse
|
||||||
from dbt.contracts.graph.manifest import Manifest, Disabled
|
from dbt.contracts.graph.manifest import Manifest, Disabled
|
||||||
from dbt.contracts.graph.compiled import (
|
from dbt.contracts.graph.nodes import (
|
||||||
CompiledResource,
|
Macro,
|
||||||
CompiledSeedNode,
|
Exposure,
|
||||||
|
Metric,
|
||||||
|
SeedNode,
|
||||||
|
SourceDefinition,
|
||||||
|
Resource,
|
||||||
ManifestNode,
|
ManifestNode,
|
||||||
)
|
)
|
||||||
from dbt.contracts.graph.parsed import (
|
|
||||||
ParsedMacro,
|
|
||||||
ParsedExposure,
|
|
||||||
ParsedMetric,
|
|
||||||
ParsedSeedNode,
|
|
||||||
ParsedSourceDefinition,
|
|
||||||
)
|
|
||||||
from dbt.contracts.graph.metrics import MetricReference, ResolvedMetricReference
|
from dbt.contracts.graph.metrics import MetricReference, ResolvedMetricReference
|
||||||
from dbt.events.functions import get_metadata_vars
|
from dbt.events.functions import get_metadata_vars
|
||||||
from dbt.exceptions import (
|
from dbt.exceptions import (
|
||||||
CompilationException,
|
CompilationException,
|
||||||
ParsingException,
|
ConflictingConfigKeys,
|
||||||
|
DisallowSecretEnvVar,
|
||||||
|
EnvVarMissing,
|
||||||
InternalException,
|
InternalException,
|
||||||
ValidationException,
|
InvalidInlineModelConfig,
|
||||||
|
InvalidNumberSourceArgs,
|
||||||
|
InvalidPersistDocsValueType,
|
||||||
|
LoadAgateTableNotSeed,
|
||||||
|
LoadAgateTableValueError,
|
||||||
|
MacroInvalidDispatchArg,
|
||||||
|
MacrosSourcesUnWriteable,
|
||||||
|
MetricInvalidArgs,
|
||||||
|
MissingConfig,
|
||||||
|
OperationsCannotRefEphemeralNodes,
|
||||||
|
PackageNotInDeps,
|
||||||
|
ParsingException,
|
||||||
|
RefBadContext,
|
||||||
|
RefInvalidArgs,
|
||||||
RuntimeException,
|
RuntimeException,
|
||||||
macro_invalid_dispatch_arg,
|
TargetNotFound,
|
||||||
missing_config,
|
ValidationException,
|
||||||
raise_compiler_error,
|
|
||||||
ref_invalid_args,
|
|
||||||
metric_invalid_args,
|
|
||||||
ref_target_not_found,
|
|
||||||
target_not_found,
|
|
||||||
ref_bad_context,
|
|
||||||
wrapped_exports,
|
|
||||||
raise_parsing_error,
|
|
||||||
disallow_secret_env_var,
|
|
||||||
)
|
)
|
||||||
from dbt.config import IsFQNResource
|
from dbt.config import IsFQNResource
|
||||||
from dbt.node_types import NodeType, ModelLanguage
|
from dbt.node_types import NodeType, ModelLanguage
|
||||||
@@ -143,7 +147,7 @@ class BaseDatabaseWrapper:
|
|||||||
raise CompilationException(msg)
|
raise CompilationException(msg)
|
||||||
|
|
||||||
if packages is not None:
|
if packages is not None:
|
||||||
raise macro_invalid_dispatch_arg(macro_name)
|
raise MacroInvalidDispatchArg(macro_name)
|
||||||
|
|
||||||
namespace = macro_namespace
|
namespace = macro_namespace
|
||||||
|
|
||||||
@@ -237,7 +241,7 @@ class BaseRefResolver(BaseResolver):
|
|||||||
elif len(args) == 2:
|
elif len(args) == 2:
|
||||||
package, name = args
|
package, name = args
|
||||||
else:
|
else:
|
||||||
ref_invalid_args(self.model, args)
|
raise RefInvalidArgs(node=self.model, args=args)
|
||||||
self.validate_args(name, package)
|
self.validate_args(name, package)
|
||||||
return self.resolve(name, package)
|
return self.resolve(name, package)
|
||||||
|
|
||||||
@@ -261,9 +265,7 @@ class BaseSourceResolver(BaseResolver):
|
|||||||
|
|
||||||
def __call__(self, *args: str) -> RelationProxy:
|
def __call__(self, *args: str) -> RelationProxy:
|
||||||
if len(args) != 2:
|
if len(args) != 2:
|
||||||
raise_compiler_error(
|
raise InvalidNumberSourceArgs(args, node=self.model)
|
||||||
f"source() takes exactly two arguments ({len(args)} given)", self.model
|
|
||||||
)
|
|
||||||
self.validate_args(args[0], args[1])
|
self.validate_args(args[0], args[1])
|
||||||
return self.resolve(args[0], args[1])
|
return self.resolve(args[0], args[1])
|
||||||
|
|
||||||
@@ -298,7 +300,7 @@ class BaseMetricResolver(BaseResolver):
|
|||||||
elif len(args) == 2:
|
elif len(args) == 2:
|
||||||
package, name = args
|
package, name = args
|
||||||
else:
|
else:
|
||||||
metric_invalid_args(self.model, args)
|
raise MetricInvalidArgs(node=self.model, args=args)
|
||||||
self.validate_args(name, package)
|
self.validate_args(name, package)
|
||||||
return self.resolve(name, package)
|
return self.resolve(name, package)
|
||||||
|
|
||||||
@@ -319,12 +321,7 @@ class ParseConfigObject(Config):
|
|||||||
if oldkey in config:
|
if oldkey in config:
|
||||||
newkey = oldkey.replace("_", "-")
|
newkey = oldkey.replace("_", "-")
|
||||||
if newkey in config:
|
if newkey in config:
|
||||||
raise_compiler_error(
|
raise ConflictingConfigKeys(oldkey, newkey, node=self.model)
|
||||||
'Invalid config, has conflicting keys "{}" and "{}"'.format(
|
|
||||||
oldkey, newkey
|
|
||||||
),
|
|
||||||
self.model,
|
|
||||||
)
|
|
||||||
config[newkey] = config.pop(oldkey)
|
config[newkey] = config.pop(oldkey)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
@@ -334,7 +331,7 @@ class ParseConfigObject(Config):
|
|||||||
elif len(args) == 0 and len(kwargs) > 0:
|
elif len(args) == 0 and len(kwargs) > 0:
|
||||||
opts = kwargs
|
opts = kwargs
|
||||||
else:
|
else:
|
||||||
raise_compiler_error("Invalid inline model config", self.model)
|
raise InvalidInlineModelConfig(node=self.model)
|
||||||
|
|
||||||
opts = self._transform_config(opts)
|
opts = self._transform_config(opts)
|
||||||
|
|
||||||
@@ -382,7 +379,7 @@ class RuntimeConfigObject(Config):
|
|||||||
else:
|
else:
|
||||||
result = self.model.config.get(name, default)
|
result = self.model.config.get(name, default)
|
||||||
if result is _MISSING:
|
if result is _MISSING:
|
||||||
missing_config(self.model, name)
|
raise MissingConfig(unique_id=self.model.unique_id, name=name)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def require(self, name, validator=None):
|
def require(self, name, validator=None):
|
||||||
@@ -404,20 +401,14 @@ class RuntimeConfigObject(Config):
|
|||||||
def persist_relation_docs(self) -> bool:
|
def persist_relation_docs(self) -> bool:
|
||||||
persist_docs = self.get("persist_docs", default={})
|
persist_docs = self.get("persist_docs", default={})
|
||||||
if not isinstance(persist_docs, dict):
|
if not isinstance(persist_docs, dict):
|
||||||
raise_compiler_error(
|
raise InvalidPersistDocsValueType(persist_docs)
|
||||||
f"Invalid value provided for 'persist_docs'. Expected dict "
|
|
||||||
f"but received {type(persist_docs)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return persist_docs.get("relation", False)
|
return persist_docs.get("relation", False)
|
||||||
|
|
||||||
def persist_column_docs(self) -> bool:
|
def persist_column_docs(self) -> bool:
|
||||||
persist_docs = self.get("persist_docs", default={})
|
persist_docs = self.get("persist_docs", default={})
|
||||||
if not isinstance(persist_docs, dict):
|
if not isinstance(persist_docs, dict):
|
||||||
raise_compiler_error(
|
raise InvalidPersistDocsValueType(persist_docs)
|
||||||
f"Invalid value provided for 'persist_docs'. Expected dict "
|
|
||||||
f"but received {type(persist_docs)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return persist_docs.get("columns", False)
|
return persist_docs.get("columns", False)
|
||||||
|
|
||||||
@@ -476,10 +467,11 @@ class RuntimeRefResolver(BaseRefResolver):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if target_model is None or isinstance(target_model, Disabled):
|
if target_model is None or isinstance(target_model, Disabled):
|
||||||
ref_target_not_found(
|
raise TargetNotFound(
|
||||||
self.model,
|
node=self.model,
|
||||||
target_name,
|
target_name=target_name,
|
||||||
target_package,
|
target_kind="node",
|
||||||
|
target_package=target_package,
|
||||||
disabled=isinstance(target_model, Disabled),
|
disabled=isinstance(target_model, Disabled),
|
||||||
)
|
)
|
||||||
self.validate(target_model, target_name, target_package)
|
self.validate(target_model, target_name, target_package)
|
||||||
@@ -497,7 +489,7 @@ class RuntimeRefResolver(BaseRefResolver):
|
|||||||
) -> None:
|
) -> None:
|
||||||
if resolved.unique_id not in self.model.depends_on.nodes:
|
if resolved.unique_id not in self.model.depends_on.nodes:
|
||||||
args = self._repack_args(target_name, target_package)
|
args = self._repack_args(target_name, target_package)
|
||||||
ref_bad_context(self.model, args)
|
raise RefBadContext(node=self.model, args=args)
|
||||||
|
|
||||||
|
|
||||||
class OperationRefResolver(RuntimeRefResolver):
|
class OperationRefResolver(RuntimeRefResolver):
|
||||||
@@ -512,13 +504,8 @@ class OperationRefResolver(RuntimeRefResolver):
|
|||||||
def create_relation(self, target_model: ManifestNode, name: str) -> RelationProxy:
|
def create_relation(self, target_model: ManifestNode, name: str) -> RelationProxy:
|
||||||
if target_model.is_ephemeral_model:
|
if target_model.is_ephemeral_model:
|
||||||
# In operations, we can't ref() ephemeral nodes, because
|
# In operations, we can't ref() ephemeral nodes, because
|
||||||
# ParsedMacros do not support set_cte
|
# Macros do not support set_cte
|
||||||
raise_compiler_error(
|
raise OperationsCannotRefEphemeralNodes(target_model.name, node=self.model)
|
||||||
"Operations can not ref() ephemeral nodes, but {} is ephemeral".format(
|
|
||||||
target_model.name
|
|
||||||
),
|
|
||||||
self.model,
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
return super().create_relation(target_model, name)
|
return super().create_relation(target_model, name)
|
||||||
|
|
||||||
@@ -541,7 +528,7 @@ class RuntimeSourceResolver(BaseSourceResolver):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if target_source is None or isinstance(target_source, Disabled):
|
if target_source is None or isinstance(target_source, Disabled):
|
||||||
target_not_found(
|
raise TargetNotFound(
|
||||||
node=self.model,
|
node=self.model,
|
||||||
target_name=f"{source_name}.{table_name}",
|
target_name=f"{source_name}.{table_name}",
|
||||||
target_kind="source",
|
target_kind="source",
|
||||||
@@ -568,7 +555,7 @@ class RuntimeMetricResolver(BaseMetricResolver):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if target_metric is None or isinstance(target_metric, Disabled):
|
if target_metric is None or isinstance(target_metric, Disabled):
|
||||||
target_not_found(
|
raise TargetNotFound(
|
||||||
node=self.model,
|
node=self.model,
|
||||||
target_name=target_name,
|
target_name=target_name,
|
||||||
target_kind="metric",
|
target_kind="metric",
|
||||||
@@ -584,9 +571,9 @@ class ModelConfiguredVar(Var):
|
|||||||
self,
|
self,
|
||||||
context: Dict[str, Any],
|
context: Dict[str, Any],
|
||||||
config: RuntimeConfig,
|
config: RuntimeConfig,
|
||||||
node: CompiledResource,
|
node: Resource,
|
||||||
) -> None:
|
) -> None:
|
||||||
self._node: CompiledResource
|
self._node: Resource
|
||||||
self._config: RuntimeConfig = config
|
self._config: RuntimeConfig = config
|
||||||
super().__init__(context, config.cli_vars, node=node)
|
super().__init__(context, config.cli_vars, node=node)
|
||||||
|
|
||||||
@@ -597,7 +584,7 @@ class ModelConfiguredVar(Var):
|
|||||||
if package_name != self._config.project_name:
|
if package_name != self._config.project_name:
|
||||||
if package_name not in dependencies:
|
if package_name not in dependencies:
|
||||||
# I don't think this is actually reachable
|
# I don't think this is actually reachable
|
||||||
raise_compiler_error(f"Node package named {package_name} not found!", self._node)
|
raise PackageNotInDeps(package_name, node=self._node)
|
||||||
yield dependencies[package_name]
|
yield dependencies[package_name]
|
||||||
yield self._config
|
yield self._config
|
||||||
|
|
||||||
@@ -690,7 +677,7 @@ class ProviderContext(ManifestContext):
|
|||||||
raise InternalException(f"Invalid provider given to context: {provider}")
|
raise InternalException(f"Invalid provider given to context: {provider}")
|
||||||
# mypy appeasement - we know it'll be a RuntimeConfig
|
# mypy appeasement - we know it'll be a RuntimeConfig
|
||||||
self.config: RuntimeConfig
|
self.config: RuntimeConfig
|
||||||
self.model: Union[ParsedMacro, ManifestNode] = model
|
self.model: Union[Macro, ManifestNode] = model
|
||||||
super().__init__(config, manifest, model.package_name)
|
super().__init__(config, manifest, model.package_name)
|
||||||
self.sql_results: Dict[str, AttrDict] = {}
|
self.sql_results: Dict[str, AttrDict] = {}
|
||||||
self.context_config: Optional[ContextConfig] = context_config
|
self.context_config: Optional[ContextConfig] = context_config
|
||||||
@@ -779,8 +766,8 @@ class ProviderContext(ManifestContext):
|
|||||||
@contextmember
|
@contextmember
|
||||||
def write(self, payload: str) -> str:
|
def write(self, payload: str) -> str:
|
||||||
# macros/source defs aren't 'writeable'.
|
# macros/source defs aren't 'writeable'.
|
||||||
if isinstance(self.model, (ParsedMacro, ParsedSourceDefinition)):
|
if isinstance(self.model, (Macro, SourceDefinition)):
|
||||||
raise_compiler_error('cannot "write" macros or sources')
|
raise MacrosSourcesUnWriteable(node=self.model)
|
||||||
self.model.build_path = self.model.write_node(self.config.target_path, "run", payload)
|
self.model.build_path = self.model.write_node(self.config.target_path, "run", payload)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@@ -795,20 +782,19 @@ class ProviderContext(ManifestContext):
|
|||||||
try:
|
try:
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
except Exception:
|
except Exception:
|
||||||
raise_compiler_error(message_if_exception, self.model)
|
raise CompilationException(message_if_exception, self.model)
|
||||||
|
|
||||||
@contextmember
|
@contextmember
|
||||||
def load_agate_table(self) -> agate.Table:
|
def load_agate_table(self) -> agate.Table:
|
||||||
if not isinstance(self.model, (ParsedSeedNode, CompiledSeedNode)):
|
if not isinstance(self.model, SeedNode):
|
||||||
raise_compiler_error(
|
raise LoadAgateTableNotSeed(self.model.resource_type, node=self.model)
|
||||||
"can only load_agate_table for seeds (got a {})".format(self.model.resource_type)
|
assert self.model.root_path
|
||||||
)
|
|
||||||
path = os.path.join(self.model.root_path, self.model.original_file_path)
|
path = os.path.join(self.model.root_path, self.model.original_file_path)
|
||||||
column_types = self.model.config.column_types
|
column_types = self.model.config.column_types
|
||||||
try:
|
try:
|
||||||
table = agate_helper.from_csv(path, text_columns=column_types)
|
table = agate_helper.from_csv(path, text_columns=column_types)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise_compiler_error(str(e))
|
raise LoadAgateTableValueError(e, node=self.model)
|
||||||
table.original_abspath = os.path.abspath(path)
|
table.original_abspath = os.path.abspath(path)
|
||||||
return table
|
return table
|
||||||
|
|
||||||
@@ -1210,7 +1196,7 @@ class ProviderContext(ManifestContext):
|
|||||||
"""
|
"""
|
||||||
return_value = None
|
return_value = None
|
||||||
if var.startswith(SECRET_ENV_PREFIX):
|
if var.startswith(SECRET_ENV_PREFIX):
|
||||||
disallow_secret_env_var(var)
|
raise DisallowSecretEnvVar(var)
|
||||||
if var in os.environ:
|
if var in os.environ:
|
||||||
return_value = os.environ[var]
|
return_value = os.environ[var]
|
||||||
elif default is not None:
|
elif default is not None:
|
||||||
@@ -1219,7 +1205,13 @@ class ProviderContext(ManifestContext):
|
|||||||
if return_value is not None:
|
if return_value is not None:
|
||||||
# Save the env_var value in the manifest and the var name in the source_file.
|
# Save the env_var value in the manifest and the var name in the source_file.
|
||||||
# If this is compiling, do not save because it's irrelevant to parsing.
|
# If this is compiling, do not save because it's irrelevant to parsing.
|
||||||
if self.model and not hasattr(self.model, "compiled"):
|
compiling = (
|
||||||
|
True
|
||||||
|
if hasattr(self.model, "compiled")
|
||||||
|
and getattr(self.model, "compiled", False) is True
|
||||||
|
else False
|
||||||
|
)
|
||||||
|
if self.model and not compiling:
|
||||||
# If the environment variable is set from a default, store a string indicating
|
# If the environment variable is set from a default, store a string indicating
|
||||||
# that so we can skip partial parsing. Otherwise the file will be scheduled for
|
# that so we can skip partial parsing. Otherwise the file will be scheduled for
|
||||||
# reparsing. If the default changes, the file will have been updated and therefore
|
# reparsing. If the default changes, the file will have been updated and therefore
|
||||||
@@ -1237,8 +1229,7 @@ class ProviderContext(ManifestContext):
|
|||||||
source_file.env_vars.append(var) # type: ignore[union-attr]
|
source_file.env_vars.append(var) # type: ignore[union-attr]
|
||||||
return return_value
|
return return_value
|
||||||
else:
|
else:
|
||||||
msg = f"Env var required but not provided: '{var}'"
|
raise EnvVarMissing(var)
|
||||||
raise_parsing_error(msg)
|
|
||||||
|
|
||||||
@contextproperty
|
@contextproperty
|
||||||
def selected_resources(self) -> List[str]:
|
def selected_resources(self) -> List[str]:
|
||||||
@@ -1274,7 +1265,7 @@ class MacroContext(ProviderContext):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
model: ParsedMacro,
|
model: Macro,
|
||||||
config: RuntimeConfig,
|
config: RuntimeConfig,
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
provider: Provider,
|
provider: Provider,
|
||||||
@@ -1389,7 +1380,7 @@ def generate_parser_model_context(
|
|||||||
|
|
||||||
|
|
||||||
def generate_generate_name_macro_context(
|
def generate_generate_name_macro_context(
|
||||||
macro: ParsedMacro,
|
macro: Macro,
|
||||||
config: RuntimeConfig,
|
config: RuntimeConfig,
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
@@ -1407,7 +1398,7 @@ def generate_runtime_model_context(
|
|||||||
|
|
||||||
|
|
||||||
def generate_runtime_macro_context(
|
def generate_runtime_macro_context(
|
||||||
macro: ParsedMacro,
|
macro: Macro,
|
||||||
config: RuntimeConfig,
|
config: RuntimeConfig,
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
package_name: Optional[str],
|
package_name: Optional[str],
|
||||||
@@ -1419,7 +1410,7 @@ def generate_runtime_macro_context(
|
|||||||
class ExposureRefResolver(BaseResolver):
|
class ExposureRefResolver(BaseResolver):
|
||||||
def __call__(self, *args) -> str:
|
def __call__(self, *args) -> str:
|
||||||
if len(args) not in (1, 2):
|
if len(args) not in (1, 2):
|
||||||
ref_invalid_args(self.model, args)
|
raise RefInvalidArgs(node=self.model, args=args)
|
||||||
self.model.refs.append(list(args))
|
self.model.refs.append(list(args))
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@@ -1427,15 +1418,21 @@ class ExposureRefResolver(BaseResolver):
|
|||||||
class ExposureSourceResolver(BaseResolver):
|
class ExposureSourceResolver(BaseResolver):
|
||||||
def __call__(self, *args) -> str:
|
def __call__(self, *args) -> str:
|
||||||
if len(args) != 2:
|
if len(args) != 2:
|
||||||
raise_compiler_error(
|
raise InvalidNumberSourceArgs(args, node=self.model)
|
||||||
f"source() takes exactly two arguments ({len(args)} given)", self.model
|
|
||||||
)
|
|
||||||
self.model.sources.append(list(args))
|
self.model.sources.append(list(args))
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
class ExposureMetricResolver(BaseResolver):
|
||||||
|
def __call__(self, *args) -> str:
|
||||||
|
if len(args) not in (1, 2):
|
||||||
|
raise MetricInvalidArgs(node=self.model, args=args)
|
||||||
|
self.model.metrics.append(list(args))
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def generate_parse_exposure(
|
def generate_parse_exposure(
|
||||||
exposure: ParsedExposure,
|
exposure: Exposure,
|
||||||
config: RuntimeConfig,
|
config: RuntimeConfig,
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
package_name: str,
|
package_name: str,
|
||||||
@@ -1454,6 +1451,12 @@ def generate_parse_exposure(
|
|||||||
project,
|
project,
|
||||||
manifest,
|
manifest,
|
||||||
),
|
),
|
||||||
|
"metric": ExposureMetricResolver(
|
||||||
|
None,
|
||||||
|
exposure,
|
||||||
|
project,
|
||||||
|
manifest,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1465,7 +1468,7 @@ class MetricRefResolver(BaseResolver):
|
|||||||
elif len(args) == 2:
|
elif len(args) == 2:
|
||||||
package, name = args
|
package, name = args
|
||||||
else:
|
else:
|
||||||
ref_invalid_args(self.model, args)
|
raise RefInvalidArgs(node=self.model, args=args)
|
||||||
self.validate_args(name, package)
|
self.validate_args(name, package)
|
||||||
self.model.refs.append(list(args))
|
self.model.refs.append(list(args))
|
||||||
return ""
|
return ""
|
||||||
@@ -1479,7 +1482,7 @@ class MetricRefResolver(BaseResolver):
|
|||||||
|
|
||||||
|
|
||||||
def generate_parse_metrics(
|
def generate_parse_metrics(
|
||||||
metric: ParsedMetric,
|
metric: Metric,
|
||||||
config: RuntimeConfig,
|
config: RuntimeConfig,
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
package_name: str,
|
package_name: str,
|
||||||
@@ -1555,7 +1558,7 @@ class TestContext(ProviderContext):
|
|||||||
def env_var(self, var: str, default: Optional[str] = None) -> str:
|
def env_var(self, var: str, default: Optional[str] = None) -> str:
|
||||||
return_value = None
|
return_value = None
|
||||||
if var.startswith(SECRET_ENV_PREFIX):
|
if var.startswith(SECRET_ENV_PREFIX):
|
||||||
disallow_secret_env_var(var)
|
raise DisallowSecretEnvVar(var)
|
||||||
if var in os.environ:
|
if var in os.environ:
|
||||||
return_value = os.environ[var]
|
return_value = os.environ[var]
|
||||||
elif default is not None:
|
elif default is not None:
|
||||||
@@ -1581,8 +1584,7 @@ class TestContext(ProviderContext):
|
|||||||
source_file.add_env_var(var, yaml_key, name) # type: ignore[union-attr]
|
source_file.add_env_var(var, yaml_key, name) # type: ignore[union-attr]
|
||||||
return return_value
|
return return_value
|
||||||
else:
|
else:
|
||||||
msg = f"Env var required but not provided: '{var}'"
|
raise EnvVarMissing(var)
|
||||||
raise_parsing_error(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_test_context(
|
def generate_test_context(
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from typing import Any, Dict, Optional
|
|||||||
from .base import BaseContext, contextmember
|
from .base import BaseContext, contextmember
|
||||||
|
|
||||||
from dbt.constants import SECRET_ENV_PREFIX, DEFAULT_ENV_PLACEHOLDER
|
from dbt.constants import SECRET_ENV_PREFIX, DEFAULT_ENV_PLACEHOLDER
|
||||||
from dbt.exceptions import raise_parsing_error
|
from dbt.exceptions import EnvVarMissing
|
||||||
|
|
||||||
|
|
||||||
SECRET_PLACEHOLDER = "$$$DBT_SECRET_START$$${}$$$DBT_SECRET_END$$$"
|
SECRET_PLACEHOLDER = "$$$DBT_SECRET_START$$${}$$$DBT_SECRET_END$$$"
|
||||||
@@ -50,8 +50,7 @@ class SecretContext(BaseContext):
|
|||||||
self.env_vars[var] = return_value if var in os.environ else DEFAULT_ENV_PLACEHOLDER
|
self.env_vars[var] = return_value if var in os.environ else DEFAULT_ENV_PLACEHOLDER
|
||||||
return return_value
|
return return_value
|
||||||
else:
|
else:
|
||||||
msg = f"Env var required but not provided: '{var}'"
|
raise EnvVarMissing(var)
|
||||||
raise_parsing_error(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_secret_context(cli_vars: Dict[str, Any]) -> Dict[str, Any]:
|
def generate_secret_context(cli_vars: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user