Compare commits

...

93 Commits

Author SHA1 Message Date
Github Build Bot
c82795a99f Bumping version to 1.4.3 and generate changelog 2023-02-24 01:35:10 +00:00
Neelesh Salian
f0f4af5c8d Fixing the release secret (#7043) 2023-02-23 17:31:04 -08:00
github-actions[bot]
3fe1502c15 Fix regression in semver comparison logic (#7040) (#7041)
(cherry picked from commit 915585c36e)

Co-authored-by: Jeremy Cohen <jeremy@dbtlabs.com>
2023-02-23 16:33:28 -08:00
FishtownBuildBot
cc2f259108 [Automated] Merged prep-release/1.4.2_4255074059 into target 1.4.latest during release process 2023-02-23 11:41:42 -06:00
Github Build Bot
51d216186d Bumping version to 1.4.2 and generate changelog 2023-02-23 17:14:29 +00:00
leahwicz
bdf7c1ff82 Revert "CT 2057 Fix compilation logic for ephemeral nodes (#7023) (#7032)" (#7035)
This reverts commit f23dbb6f6b.
2023-02-23 12:09:33 -05:00
github-actions[bot]
f23dbb6f6b CT 2057 Fix compilation logic for ephemeral nodes (#7023) (#7032)
* Don't overwrite sql in extra_ctes when compiling (rendering) nodes

(cherry picked from commit 70c26f5c74)

Co-authored-by: Gerda Shank <gerda@dbtlabs.com>
2023-02-22 18:05:00 -05:00
FishtownBuildBot
ec17ecec86 [Automated] Merged prep-release/1.4.2rc2_4246496113 into target 1.4.latest during release process 2023-02-22 14:36:47 -06:00
Github Build Bot
a3d326404d Bumping version to 1.4.2rc2 and generate changelog 2023-02-22 20:06:40 +00:00
Peter Webb
c785f320ea CT-2160: Fix logbook (legacy logging) regression (#7029)
* CT-2160: Fix logbook (legacy logging) regression

* CT-2160: Changelog entry
2023-02-22 14:19:10 -05:00
FishtownBuildBot
1fe0c32890 [Automated] Merged prep-release/1.4.2rc1_4187540534 into target 1.4.latest during release process 2023-02-15 14:15:08 -06:00
Github Build Bot
11752edef8 Bumping version to 1.4.2rc1 and generate changelog 2023-02-15 19:47:50 +00:00
github-actions[bot]
16cb498b56 [Backport 1.4.latest] Set relation_name in tests at compile time (#6978)
* Set relation_name in tests at compile time (#6949)

(cherry picked from commit 480e0e55c5)

* Tweak artifact test

---------

Co-authored-by: Gerda Shank <gerda@dbtlabs.com>
2023-02-14 18:33:12 -05:00
Gerda Shank
03f6655f1a Remove empty changie file (#6977) 2023-02-14 15:52:35 -05:00
Gerda Shank
8dc3d0a531 Fix state comparison with disabled exposure (or metric) (#6967) 2023-02-13 21:55:18 -05:00
github-actions[bot]
c172687736 CT 2000 fix semver prerelease comparisons (#6838) (#6958)
* Modify semver.py to not use packaging.version.parse

* Changie

(cherry picked from commit d9424cc710)

Co-authored-by: Gerda Shank <gerda@dbtlabs.com>
2023-02-13 22:39:59 +01:00
Emily Rockman
8a31eab181 Add most recent dbt-docs changes (#6923) (#6925)
* Add new index.html and changelog yaml files from dbt-docs

* Update .changes/unreleased/Docs-20230209-082901.yaml

---------

Co-authored-by: FishtownBuildBot <77737458+FishtownBuildBot@users.noreply.github.com>
2023-02-10 11:14:33 -06:00
Peter Webb
4d0ee2fc47 Ensure flush() after logging write() (#6909) (#6922)
* ct-2063: Ensure flush after logging, by using Python's logging subsystem directly

* ct-2063: Add changelog entry
2023-02-09 10:48:30 -05:00
github-actions[bot]
ddb2f0f71d [Backport 1.4.latest] Add back depends_on for seeds - only macros, never nodes (#6920)
* Add back `depends_on` for seeds - only `macros`, never `nodes` (#6851)

* Extend functional tests for seeds w hooks

* Add MacroDependsOn to seeds, raise exception for other deps

* Add changelog entry

* Fix unit tests

* Update upgrade_seed_content

* Cleanup

* Regen manifest v8 schema. Fix tests

* Be less magical

* PR feedback

(cherry picked from commit 298bf8a1d4)

* Update manifest v8 for v1.4.x

* Cleanup

---------

Co-authored-by: Jeremy Cohen <jeremy@dbtlabs.com>
2023-02-09 12:37:46 +01:00
Emily Rockman
2bb2e73df9 1.4 regression: Check if status has node attribute (#6899) (#6904)
* check for node

* add changelog

* add test for regression
2023-02-08 15:00:32 -06:00
Peter Webb
790fecab92 CT-1917: Fix a regression in the behavior of the -q/--quiet cli parameter (#6886) (#6889) 2023-02-08 10:38:42 -05:00
Emily Rockman
f6926c0ed6 [BACKPORT] 1.4.latest update regex to match all iterations (#6839) (#6846)
* update regex to match all iterations (#6839)

* update regex to match all iterations

* convert to num to match all adapters

* add comments, remove extra .

* clarify with more comments

* Update .bumpversion.cfg

Co-authored-by: Nathaniel May <nathaniel.may@fishtownanalytics.com>

---------

Co-authored-by: Nathaniel May <nathaniel.may@fishtownanalytics.com>
# Conflicts:
#	.bumpversion.cfg

* put back correct version

* put back correct version
2023-02-06 13:54:34 -06:00
Alexander Smolyakov
52be5ffaa6 [CI/CD] Backport release workflow to 1.4.latest (#6793)
* [CI/CD] Update release workflow and introduce workflow for nightly releases (#6602)

* Add release workflows

* Update nightly-release.yml

* Set default `test_run` value to `true`

* Update .bumpversion.cfg

* Resolve review comment

- Update workflow docs
- Change workflow name
- Set `test_run` default value to `true`

* Update Slack secret

* PyPI

* Update release workflow (#6778)

- Update AWS secrets
- Rework condition for Slack notification

---------

Co-authored-by: Emily Rockman <emily.rockman@dbtlabs.com>
2023-02-01 14:20:33 -06:00
Emily Rockman
31bfff81d9 fix contributor list generation (#6799) (#6808) 2023-01-31 21:39:13 -06:00
github-actions[bot]
3fbfa6e6f3 [Backport 1.4.latest] CT 1894 log partial parsing var changes and sort cli vars before hashing (#6758)
* CT 1894 log partial parsing var changes and sort cli vars before hashing (#6713)

* Log information about vars_hash, normalize cli_vars before hashing

* Changie
2023-01-26 15:04:25 -05:00
github-actions[bot]
165035692f Bumping version to 1.4.1 and generate changelog (#6743)
* Bumping version to 1.4.1rc1 and generate CHANGELOG

* Bumping version to 1.4.1 and generate CHANGELOG (#6744)

Co-authored-by: Github Build Bot <buildbot@fishtownanalytics.com>

* Adjust docker version image

Co-authored-by: Github Build Bot <buildbot@fishtownanalytics.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: leahwicz <60146280+leahwicz@users.noreply.github.com>
2023-01-25 20:48:44 -05:00
github-actions[bot]
80f76e9e6e change exposure_content to source_content (#6739) (#6740)
* change `exposure_content` to `source_content`

* Adding changelog

Co-authored-by: Leah Antkiewicz <leah.antkiewicz@fishtownanalytics.com>
(cherry picked from commit b0651b13b5)

Co-authored-by: Matthew Beall <matthew@beall.org>
2023-01-25 20:11:38 -05:00
github-actions[bot]
a2e7249c44 Bumping version to 1.4.0 and generate CHANGELOG (#6727)
Co-authored-by: Github Build Bot <buildbot@fishtownanalytics.com>
2023-01-25 10:17:36 -05:00
github-actions[bot]
9700ff1866 CT 1886 include adapter_response in NodeFinished log message (#6709) (#6714)
* Include adapter_response in run_result in NodeFinished log event

* Changie

(cherry picked from commit e2ccf011d9)

Co-authored-by: Gerda Shank <gerda@dbtlabs.com>
2023-01-24 15:38:51 -05:00
colin-rogers-dbt
e61a39f27f mv on_schema_change tests -> "adapter zone" (#6618) (#6686)
* mv `on_schema_change` tests -> "adapter zone" (#6618)

* Mv incremental on_schema_change tests to 'adapter zone'

* Use type_string()

* Cleanup

* mv `on_schema_change` tests -> "adapter zone" (#6618)

* Mv incremental on_schema_change tests to 'adapter zone'

* Use type_string()

* Cleanup

Co-authored-by: Jeremy Cohen <jeremy@dbtlabs.com>
2023-01-20 12:26:32 -08:00
github-actions[bot]
d934e713db Bumping version to 1.4.0rc2 and generate CHANGELOG (#6661)
Co-authored-by: Github Build Bot <buildbot@fishtownanalytics.com>
2023-01-19 12:13:06 -05:00
github-actions[bot]
ef9bb925d3 add backwards compatibility and default argument for incremental_predicates (#6628) (#6660)
* add backwards compatibility and default argument

* changie <3

* Update .changes/unreleased/Fixes-20230117-101342.yaml

Co-authored-by: Jeremy Cohen <jeremy@dbtlabs.com>
(cherry picked from commit f841a7ca76)

Co-authored-by: dave-connors-3 <73915542+dave-connors-3@users.noreply.github.com>
2023-01-19 10:24:49 -05:00
github-actions[bot]
f73359b87c [Backport 1.4.latest] convert 062_defer_state_tests (#6657)
* convert 062_defer_state_tests (#6616)

* Fix --favor-state flag

* Convert 062_defer_state_tests

* Revert "Fix --favor-state flag"

This reverts commit ccbdcbad98b26822629364e6fdbd2780db0c20d3.

* Reformat

* Revert "Revert "Fix --favor-state flag""

This reverts commit fa9d2a09d6.

(cherry picked from commit 07a004b301)

* Add changelog entry

Co-authored-by: Jeremy Cohen <jeremy@dbtlabs.com>
2023-01-19 13:27:23 +01:00
Emily Rockman
b4706c4dec finish message rename in types.proto (#6594) (#6596)
* finish message rename in types.proto

* add new parameter
2023-01-13 10:20:34 -06:00
github-actions[bot]
b46d35c13f Call update_event_status earlier + rename an event (#6572) (#6591)
* Rename HookFinished -> FinishedRunningStats

* Move update_event_status earlier when node finishes

* Add changelog entry

* Add update_event_status for skip

* Update changelog entry

(cherry picked from commit 86e8722cd8)

Co-authored-by: Jeremy Cohen <jeremy@dbtlabs.com>
2023-01-13 11:53:14 +01:00
github-actions[bot]
eba90863ed Bumping version to 1.4.0rc1 and generate CHANGELOG (#6569)
Co-authored-by: Github Build Bot <buildbot@fishtownanalytics.com>
2023-01-10 22:04:55 -05:00
Michelle Ark
7a61602738 Add --warn-error-options (#6520)
* adding --warn-error-options

Co-authored-by: Github Build Bot <buildbot@fishtownanalytics.com>
2023-01-10 19:43:52 -05:00
Doug Beatty
dd4b47d8b1 Check length of escaped strings in the adapter test (#6567)
* Check length of escaped strings in the adapter test

* One column per line to improve readability
2023-01-10 17:26:35 -07:00
Emily Rockman
eb200b4687 Rename exceptions (#6539)
* rename InternalException

* rename RuntimeException

* rename DatabaseException

* rename CompilationException

* cleanup renames in tests and postgres

* rename ValidationException

* rename IncompatibleSchemaException

* more renaming

* more renaming

* rename InternalException again

* convert ParsingException

* replace JSONValidationException and SemverException

* replace VersionsNotCompatibleException

* replace NotImplementedException

* replace FailedToConnectException

* replace InvalidConnectionException

* replace InvalidSelectorException

* replace DuplicateYamlKeyException

* replace ConnectionException

* minor cleanup

* update comment

* more cleanup

* add class decorator

* rename more exceptions

* more renamed, add changelog

* rename exception

* rework class deprecations

* removing testing line

* fix failing test

* rename newer exceptions

* fix failing test

* commit unsaved faile

* convert back an rpc exception

* remove class deprecations
2023-01-10 17:43:27 -06:00
mivanicova
0fc080d222 add defer_to_manifest in before_run to fix faulty deferred docs generate (#6488)
* add defer_to_manifest in before_run to fix faulty deferred docs generate

* add a changelog

* add declaration of defer_to_manifest to FreshnessTask and GraphRunnableTask

* fix: add defer_to_manifest method  to ListTask
2023-01-10 15:18:20 -08:00
AGPapa
5da63602b3 Adds buildable selection mode (#6366) 2023-01-10 15:15:48 -08:00
nshuman1
457ff3ef48 fixed minor typos (#6499)
* fixed minor typos

* adding changelog file

* linter

* Update Docs-20230102-170216.yaml
2023-01-10 13:36:21 -08:00
Gerda Shank
0dbdecef10 Ct 1716 cleanup logging events (#6561)
* Combine DbtProfileError log events

* Combine DbtProjectErrorException with DbtProjectError

* Combine cache logging events

* Changie

* fix ticket number

* Ooops. Add another file.

* fix serialization of profile names
2023-01-10 15:56:52 -05:00
Doug Beatty
b13b0e9492 Re-factor list of YAML keys for hooks to late-render (#6435)
* Re-factor list of YAML keys for hooks to late-render

* Add `pre_` and `post_hook` to list of late-rendered hooks

* Check for non-empty set intersection

Co-authored-by: Kshitij Aranke <kshitij.aranke@dbtlabs.com>

* Test functional synonymy of `*_hook` with `*-hook`

Test that `pre_hook`/`post_hook` are functionally synonymous with `pre-hook`/`post-hook` for model project config

* Undo bugfix to validate the new test fails

* Revert "Undo bugfix to validate the new test fails"

This reverts commit e83a2be2eb.

Co-authored-by: Kshitij Aranke <kshitij.aranke@dbtlabs.com>
2023-01-10 12:35:06 -07:00
Tim Mastny
b9fdfd9e36 Adds the meta field to node_info in logs (#6493)
* add meta attribute to nodeinfo for events

* also add meta to dataclass

* add to unit test to ensure meta is added

* adding functional test to check that meta is passed to nodeinfo during logging

* changelog

* remove used imported

* add tests with non-string keys

* renaming test dict keys

* add non-string value

* resolve failing test

* test additional non-string values

* fix flake8

* Stringify meta dict in node_info

Co-authored-by: Gerda Shank <gerda@dbtlabs.com>
2023-01-10 13:42:51 -05:00
Peter Webb
4d6352db14 CT-1645: Bump mashumaro version to receive regression fix, and add unit test to confirm fix. (#6564) 2023-01-10 13:14:24 -05:00
Doug Beatty
9eb82c6497 Remove duplicated changelog entry for 5521 (#6563) 2023-01-10 11:13:50 -07:00
Jeremy Cohen
89cc89dfdf Fix changelog entry for 5989/6432 (#6560) 2023-01-10 06:57:39 -07:00
Kshitij Aranke
2b0f6597a4 [CT-1694] Deprecate event tracking tests (#6538) 2023-01-09 15:23:02 -08:00
Peter Webb
294def205f Fix event level regression (#6556)
* CT-6501: Make certain disabled test events debug-level again to match historical behavior.

* CT-1740: Add changelog entry.
2023-01-09 18:15:59 -05:00
Emily Rockman
34fa703466 fix log msg format (#6557) 2023-01-09 15:56:42 -06:00
Mila Page
ab3f8dcbfd Ct 1517/convert 060 persist docs (#6409)
* convert the test and fix an error due to a dead code seed

* Get rid of old test

* Remove unfortunately added files. Don't use that *

Co-authored-by: Mila Page <versusfacit@users.noreply.github.com>
2023-01-09 11:07:34 -08:00
Emily Rockman
02c20477b9 add deprecation decorator (#6540)
* add derecation decorator

* fix tests

* updated changelog and deprecation reason
2023-01-09 12:09:12 -06:00
Gerda Shank
d9a4ee126a CT 1549 reorg logging events to have two top level keys (#6553)
* Update types.proto

* pre-commit passes

* Cleanup tests and tweak EventLevels

* Put node_info back on SQLCommit. Add "level" to fire_event function.

* use event.message() in warn_or_error

* Fix logging test

* Changie

* Fix a couple of unit tests

* import Protocol from typing_extensions for 3.7
2023-01-09 11:33:44 -05:00
Kshitij Aranke
94d6d19fb4 [CT-1693] Port severity test to Pytest (#6537) 2023-01-06 11:59:53 -08:00
Callum McCann
d43c070007 Making Metric Timestamps Optional (#6402)
* changing to optional

* adding tests

* tests and changie

* pre-commit cleaning

* formatting fixes

* pre-commit update
2023-01-05 15:49:55 -05:00
Callum McCann
9ef236601b Updating metric attributes renamed event (#6518)
* updating message

* adding changie
2023-01-05 15:49:44 -05:00
justbldwn
9d6f961d2b 🎨 add missing fstrings, convert format to fstring (#6243)
Co-authored-by: Gerda Shank <gerda@dbtlabs.com>
2023-01-05 15:28:27 -05:00
Niall Woodward
5453840950 DBT_FAVOR_STATE_STATE -> DBT_FAVOR_STATE (#6392)
* DBT_FAVOR_STATE_STATE -> DBT_FAVOR_STATE

* add changelog
2023-01-04 15:53:35 -08:00
Gerda Shank
d453964546 CT 1551 partial parsing logging events (#6474) 2023-01-04 14:28:26 -05:00
dependabot[bot]
748a932811 Update agate requirement from <1.6.4,>=1.6 to >=1.6,<1.7.1 in /core (#6506)
* Update agate requirement from <1.6.4,>=1.6 to >=1.6,<1.7.1 in /core

Updates the requirements on [agate](https://github.com/wireservice/agate) to permit the latest version.
- [Release notes](https://github.com/wireservice/agate/releases)
- [Changelog](https://github.com/wireservice/agate/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/wireservice/agate/compare/1.6.0...1.7.0)

---
updated-dependencies:
- dependency-name: agate
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* Add automated changelog yaml from template for bot PR

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Github Build Bot <buildbot@fishtownanalytics.com>
2023-01-04 09:02:37 -08:00
Jeremy Cohen
8217ad4722 Fix issue link for 5859 changelog entry (#6496) 2023-01-04 10:46:52 +01:00
Gerda Shank
6ef3fbbf76 Partial parsing bug with empty schema file - ensure None is not passed to load_yaml_text (#6494) 2023-01-03 17:14:56 -05:00
justbldwn
76fd12c7cd adding pre-commit install to make dev (#6417)
*  adding pre-commit install to make dev

* 🎨 updating format of Makefile and CONTRIBUTING.md

* 📝 adding changelog via changie new

*  adding dev_req to Makefile + docs

* 🎨 remove dev_req from docs, dry makefile

* Align names of `.PHONY` targets with their associated rules

Co-authored-by: Doug Beatty <44704949+dbeatty10@users.noreply.github.com>
Co-authored-by: Doug Beatty <doug.beatty@dbtlabs.com>
2023-01-03 11:18:55 -07:00
Doug Beatty
9ecb6e50e4 Treat dense text blobs as binary for git grep (#6462) 2022-12-20 17:10:01 -07:00
Kshitij Aranke
ce9d0afb8a [CT-1591] Don't parse empty Python files (#6433) 2022-12-20 10:56:36 -08:00
Emily Rockman
c39ea807e8 add back validator_error_message (#6465) 2022-12-20 10:56:08 -06:00
Ikko Ashimine
1e35339389 Fix typo in util.py (#6037)
* Fix typo in util.py

identifer -> identifier

* Add change log

* Update .changes/unreleased/Fixes-20221117-220320.yaml

Co-authored-by: Emily Rockman <ebuschang@gmail.com>
2022-12-19 20:01:19 -06:00
Emily Rockman
304797b099 exception cleanup (#6347)
* starting to move jinja exceptions

* convert some exceptions

* add back old functions for backward compatibility

* organize

* more conversions

* more conversions

* add changelog

* split out CacheInconsistency

* more conversions

* convert even more

* convert parsingexceptions

* fix tests

* more conversions

* more conversions

* finish converting exception functions

* convert more tests

* standardize to msg

* remove some TODOs

* fix test param and check the rest

* add comment, move exceptions

* add types

* fix type errors

* fix type for adapter_response

* remove 0.13 version from message
2022-12-19 19:36:24 -06:00
Emily Rockman
b9bdb775ab Fix dependency changelog kind (#6452) 2022-12-19 13:13:38 -06:00
leahwicz
df93858b4b Updating 1.4.0b1 changelog (#6450) 2022-12-15 09:55:23 -05:00
dave-connors-3
e8da84fb9e Feature/support incremental predicates (#5702)
* pass predicated to merge strategy

* postgres delete and insert

* merge with predicates

* update to use arbitrary list of predicates, not dictionaries, merge and delete

* changie

* add functional test to adapter zone

* comma in test config

* add test for incremental predicates delete and insert postgres

* update test structure for inheritance

* handle predicates config for backwards compatibility

* test for predicates keyword

* Add generated CLI API docs

Co-authored-by: Colin <colin.rogers@dbtlabs.com>
Co-authored-by: Github Build Bot <buildbot@fishtownanalytics.com>
2022-12-14 14:07:41 -08:00
Peter Webb
7e90e067af Paw/ct 1652 restore default logging (#6447)
* CT-1652: Restore stdout logging before logger is fully configured

* CT-1652: Add changelog entry

* CT-1652: formatting fix for black
2022-12-14 17:04:50 -05:00
Peter Webb
5e4e917de5 CT-1685: Restore certain aspects of legacy logging behavior important… (#6443)
* CT-1685: Restore certain aspects of legacy logging behavior important to dbt-rpc

* CT-1658: And changelog entry
2022-12-14 11:13:34 -05:00
github-actions[bot]
05dc0212e7 Bumping version to 1.4.0b1 and generate changelog (#6440)
* Bumping version to 1.4.0b1 and generate CHANGELOG

* Updating date

* Updating date

Co-authored-by: Github Build Bot <buildbot@fishtownanalytics.com>
Co-authored-by: leahwicz <60146280+leahwicz@users.noreply.github.com>
2022-12-13 20:18:11 -05:00
Gerda Shank
c00052cbfb Add Optional back on "database" field of HasRelationMetadata (#6439) 2022-12-13 18:15:25 -05:00
Kshitij Aranke
3d54a83822 [CT-1284] Change Python model default materialization to table (#6432) 2022-12-13 15:07:56 -08:00
Gerda Shank
fafd5edbda CT 1644 node cleanup (#6427)
* Remove unneeded SQL compilation attributes from SeedNode

* Fix various places that referenced removed attributes

* Cleanup a few Unions

* More formatting in nodes.py

* Mypy passing. Untested.

* Unit tests working

* use "doc" in documentation unique_ids

* update some doc_ids

* Fix some artifact tests. Still need previous version.

* Update manifest/v8.json

* Move relation_names to parsing

* Fix a couple of tests

* Update some artifacts. snapshot_seed has wrong schema.

* Changie

* Tweak NodeType.Documentation

* Put store_failures property in the right place

* Fix setting relation_name
2022-12-13 12:39:35 -05:00
Josh Devlin
8478262580 Update docker README (#6423) 2022-12-13 11:12:34 -05:00
Kshitij Aranke
83b1fee062 Add aranke to core committers (#6431) 2022-12-12 15:13:03 -08:00
Emily Rockman
0fbbc896b2 Remove PR from most changelog kinds (#6374)
* update changie to require issue or pr, and allow multiple

* remove extraneous data from changelog files.

* allow for multiple PR/issues to be entered

* update contributing guide

* remove issue number from bot changelogs

* update format of PR

* fix dependency changelogs

* remove extra line

* remove extra lines, tweak contributor wording

* Update CONTRIBUTING.md

Co-authored-by: Doug Beatty <44704949+dbeatty10@users.noreply.github.com>

Co-authored-by: Doug Beatty <44704949+dbeatty10@users.noreply.github.com>
2022-12-12 13:18:15 -06:00
Ian Knox
0544b08543 Add support for Python 3.11 (#6326)
* Get running with Python 3.11

* More tests passing, mypy still unhappy

* Upgrade to 3.11, and bump mashumaro

* patch importlib.import_module last

* lambda: Policy() default_factory on include and quote policy

* Add changelog entry

* Put a lambda on it

* Fix text formatting for log file

* Handle variant type return from e.log_level()

Co-authored-by: Jeremy Cohen <jeremy@dbtlabs.com>
Co-authored-by: Josh Taylor <joshuataylorx@gmail.com>
Co-authored-by: Michelle Ark <michelle.ark@dbtlabs.com>
2022-12-08 18:34:03 +01:00
bruno messias
bef6edb942 Fix dbt.config.get default values (python-model) (#6317)
* feat: add a list of default values to the ctx manager

* tests: dbt.get.config default values

* feat: validate the num of args in config.get

* feat: jinja template for dbt.config.get default values

* docs: changie yaml

* fix:typo on error message

Co-authored-by: Chenyu Li <chenyulee777@gmail.com>

Co-authored-by: Chenyu Li <chenyulee777@gmail.com>
2022-12-07 15:52:38 -08:00
timle
99f27de934 Feature/dbt deps tarball (#4689)
* v0 - new dbt deps type: tarball url

in support of
https://github.com/dbt-labs/dbt-core/issues/4205

* flake8 fixes

* adding max size tarball condition

* clean up imports

* typing

* adding sha1 and subdirectory options; improve logging feedback

sha1: allow user to specify sha1 in packages.yaml, will only install if package matches
subdirectory: allow user to specify subdirectory of package in tarfile, if the package is a non standard structure (like with git subdirectory option)

* simple tests added

* flake fixes

* changes to support tests; adding exceptions; fire_event logging

* new logging events

* tarball exceptions added

* build out tests

* removing in memory tarball test

* update type codes to M - Misc

* adding new events to test_events

* fix spacing for flake

* add retry download code - as used in registry calls

* clean

* remove saving tar in memory inside tarfile object

will hit url multiple times instead

* remove duplicative code after refactor

* black updates

* black formatting

* black formatting

* refactor - no more in-memory tarfile - all as file operations now

- remove tarfile passing, always use tempfile instead
- reorganize system.* functions, removing duplicative code
- more notes on current flow and structure - esp need for pattern of 1) unpack 2) scan for package dir 3) copy to destination.
- cleaning

* cleaning and sync to new tarball code

* cleaning and sync to new tarball code

* requested changes from PR

https://github.com/dbt-labs/dbt-core/pull/4689#discussion_r812970847

* reversions from revision 2

removing sha1 check to simplify/mirror hub install pattern

* simplify/mirror hub install pattern

to simplify/mirror hub install pattern
- removing sha1 check
- supply name/version to act as our 'metadata' source

* simplify/mirror hub install pattern

simplify with goal of mirroring hub install pattern
- supporting subfolders like git packages, and sha1 checks are removed
- existing code from RegistryPinnedPackage (install() and download_and_untar()) performs the operations
- RegistryPinnedPackage install() and download_and_untar() are not currently set up as functions that can be used across classes - this should be moved to dbt.deps.base, or to a dbt.deps.common file - need dbt labs feedback on how to proceed (or leave as is)

* remove revisions, no longer doing package check

* slim down to basic tests

more complex features have been removed (sha1, subfolder) so testing is much simpler!

* fix naming to match hubs behavior

remove version from package folder name

* refactor install and download to upstream PinnedPackage class

i'm on the fence if this is right approach, but seems like most sensible after some thought

* Create Features-20221107-105018.yaml

* fix flake, black, mypy errors

* additional flake/black fixes

* Update .changes/unreleased/Features-20221107-105018.yaml

fix username on changelog

Co-authored-by: Emily Rockman <ebuschang@gmail.com>

* change to fstring

Co-authored-by: Emily Rockman <ebuschang@gmail.com>

* cleaning - remove comment

* remove comment/question for dbt team

* in support of issuecomment 1334055944

https://github.com/dbt-labs/dbt-core/pull/4689#issuecomment-1334055944

* in support of issuecomment 1334118433

https://github.com/dbt-labs/dbt-core/pull/4689#issuecomment-1334118433

* black fixes; remove debug bits

* remove `.format` & add 'tarball' as version

'tarball' as version so that the temp files format nicely:
[tempfile_location]/dbt_utils_2..tar.gz # old
vs
[tempfile_location]/dbt_utils_1.tarball.tar.gz # current

* port os.path refs in `PinnedPackage._install` to pathlib

* lowercase as per PR feedback

* update tests after removing version arg

goes along with 8787ba41af

Co-authored-by: Emily Rockman <ebuschang@gmail.com>
2022-12-07 15:48:17 -06:00
Jeremy Cohen
9c91f3a7bd Adjust tox passenv to be multiline (#6405) 2022-12-07 22:47:51 +01:00
Gerda Shank
1b6fed2ffd CT 1604 remove compiled classes (#6384)
* removed Compiled versions of nodes

* Remove compiled fields from dictionary if not compiled

* check compiled is False instead of attribute existence in env_var
processing

* Update artifacts test (CompiledSnapshotNode did not have SnapshotConfig)

* Changie

* more complicated 'compiling' check in env_var

* Update test_exit_codes.py
2022-12-07 15:21:05 -05:00
dependabot[bot]
0721f2c1b7 Bump mashumaro[msgpack] from 3.1.1 to 3.2 in /core (#6375)
* Bump mashumaro[msgpack] from 3.1.1 to 3.2 in /core

Bumps [mashumaro[msgpack]](https://github.com/Fatal1ty/mashumaro) from 3.1.1 to 3.2.
- [Release notes](https://github.com/Fatal1ty/mashumaro/releases)
- [Commits](https://github.com/Fatal1ty/mashumaro/compare/v3.1.1...v3.2)

---
updated-dependencies:
- dependency-name: mashumaro[msgpack]
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Add automated changelog yaml from template for bot PR

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Github Build Bot <buildbot@fishtownanalytics.com>
2022-12-07 10:16:31 -05:00
Doug Beatty
b9a35da118 Fix intermittent database connection failure in Windows CI test (#6395)
* Fix intermittent database connection failure in Windows CI test

* Changelog entry
2022-12-06 18:34:39 -07:00
Peter Webb
60f80056b1 CT-1405: Refactor event logging code (#6291)
* CT-1405: Refactor event logging code

* CT-1405: Add changelog entry

* CT-1405: Add code to protect against using closed streams from past tests.

* CT-1405: Restore unit test which was only failing locally

* CT-1405: Document a hack with issue # to resolve it in the future

* CT-1405: Make black happy

* CT-1405: Get rid of confusing factory function and duplicated function

* CT-1405: Remove unused event from types.proto and auto-gen'd file
2022-12-06 15:51:52 -05:00
Stu Kilgore
540c3b79aa Prevent docs gen workflow on forks (#6390) 2022-12-06 11:14:20 -06:00
Gerda Shank
16f529e1d4 CT 1477 enrich logging events with data similar to legacy logger (#6325) 2022-12-02 19:29:25 -05:00
Doug Beatty
ebfcf2a9ef Update core/dbt/README.md to match current (#6371)
* Update core/dbt/README.md to match current

Add missing files/folders and alphabetize

* Changelog entry
2022-12-02 15:45:53 -07:00
Alexander Butler
67a8138b65 [fix] Fix the partial parse write path (#6081)
* Fix the partial parse path

Partial parse should use project root or it does not resolve to correct path. 
Eg. `target-path: ../some/dir/target`, if not ran from root, creates an erroneous folder.

* Run pre-commit

* Changie

Co-authored-by: Gerda Shank <gerda@dbtlabs.com>
2022-12-02 17:23:37 -05:00
340 changed files with 14234 additions and 25538 deletions

View File

@@ -1,13 +1,15 @@
[bumpversion]
current_version = 1.4.0a1
parse = (?P<major>\d+)
\.(?P<minor>\d+)
\.(?P<patch>\d+)
((?P<prekind>a|b|rc)
(?P<pre>\d+) # pre-release version num
current_version = 1.4.3
parse = (?P<major>[\d]+) # major version number
\.(?P<minor>[\d]+) # minor version number
\.(?P<patch>[\d]+) # patch version number
(((?P<prekind>a|b|rc) # optional pre-release type
?(?P<num>[\d]+?)) # optional pre-release version number
\.?(?P<nightly>[a-z0-9]+\+[a-z]+)? # optional nightly release indicator
)?
serialize =
{major}.{minor}.{patch}{prekind}{pre}
{major}.{minor}.{patch}{prekind}{num}.{nightly}
{major}.{minor}.{patch}{prekind}{num}
{major}.{minor}.{patch}
commit = False
tag = False
@@ -21,9 +23,11 @@ values =
rc
final
[bumpversion:part:pre]
[bumpversion:part:num]
first_value = 1
[bumpversion:part:nightly]
[bumpversion:file:core/setup.py]
[bumpversion:file:core/dbt/version.py]

126
.changes/1.4.0.md Normal file
View File

@@ -0,0 +1,126 @@
## dbt-core 1.4.0 - January 25, 2023
### Breaking Changes
- Cleaned up exceptions to directly raise in code. Also updated the existing exception to meet PEP guidelines.Removed use of all exception functions in the code base and marked them all as deprecated to be removed next minor release. ([#6339](https://github.com/dbt-labs/dbt-core/issues/6339), [#6393](https://github.com/dbt-labs/dbt-core/issues/6393), [#6460](https://github.com/dbt-labs/dbt-core/issues/6460))
### Features
- Added favor-state flag to optionally favor state nodes even if unselected node exists ([#5016](https://github.com/dbt-labs/dbt-core/issues/5016))
- 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))
- incremental predicates ([#5680](https://github.com/dbt-labs/dbt-core/issues/5680))
- Friendlier error messages when packages.yml is malformed ([#5486](https://github.com/dbt-labs/dbt-core/issues/5486))
- 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))
- Making timestamp optional for metrics ([#6398](https://github.com/dbt-labs/dbt-core/issues/6398))
- The meta configuration field is now included in the node_info property of structured logs. ([#6216](https://github.com/dbt-labs/dbt-core/issues/6216))
- Adds buildable selection mode ([#6365](https://github.com/dbt-labs/dbt-core/issues/6365))
- --warn-error-options: Treat warnings as errors for specific events, based on user configuration ([#6165](https://github.com/dbt-labs/dbt-core/issues/6165))
### 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))
- fix missing f-strings, convert old .format() messages to f-strings for consistency ([#6241](https://github.com/dbt-labs/dbt-core/issues/6241))
- Clarify Error Message for how many models are allowed in a Python file ([#6245](https://github.com/dbt-labs/dbt-core/issues/6245))
- Fix typo in util.py ([#4904](https://github.com/dbt-labs/dbt-core/issues/4904))
- 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))
- add pre-commit install to make dev script in Makefile ([#6269](https://github.com/dbt-labs/dbt-core/issues/6269))
- Late-rendering for `pre_` and `post_hook`s in `dbt_project.yml` ([#6411](https://github.com/dbt-labs/dbt-core/issues/6411))
- [CT-1284] Change Python model default materialization to table ([#5989](https://github.com/dbt-labs/dbt-core/issues/5989))
- [CT-1591] Don't parse empty Python files ([#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))
- fix docs generate --defer by adding defer_to_manifest to before_run ([#6488](https://github.com/dbt-labs/dbt-core/issues/6488))
- Bug when partial parsing with an empty schema file ([#4850](https://github.com/dbt-labs/dbt-core/issues/4850))
- Fix DBT_FAVOR_STATE env var ([#5859](https://github.com/dbt-labs/dbt-core/issues/5859))
- Restore historical behavior of certain disabled test messages, so that they are at the less obtrusive debug level, rather than the warning level. ([#6501](https://github.com/dbt-labs/dbt-core/issues/6501))
- Bump mashumuro version to get regression fix and add unit test to verify that fix. ([#6428](https://github.com/dbt-labs/dbt-core/issues/6428))
- Call update_event_status earlier for node results. Rename event 'HookFinished' -> FinishedRunningStats ([#6571](https://github.com/dbt-labs/dbt-core/issues/6571))
- Provide backward compatibility for `get_merge_sql` arguments ([#6625](https://github.com/dbt-labs/dbt-core/issues/6625))
- Fix behavior of --favor-state with --defer ([#6617](https://github.com/dbt-labs/dbt-core/issues/6617))
- Include adapter_response in NodeFinished run_result log event ([#6703](https://github.com/dbt-labs/dbt-core/issues/6703))
### 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))
- Updated minor typos encountered when skipping profile setup ([dbt-docs/#6529](https://github.com/dbt-labs/dbt-docs/issues/6529))
### 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))
- Treat dense text blobs as binary for `git grep` ([#6294](https://github.com/dbt-labs/dbt-core/issues/6294))
- Prune partial parsing logging events ([#6313](https://github.com/dbt-labs/dbt-core/issues/6313))
- Updating the deprecation warning in the metric attributes renamed event ([#6507](https://github.com/dbt-labs/dbt-core/issues/6507))
- [CT-1693] Port severity test to Pytest ([#6466](https://github.com/dbt-labs/dbt-core/issues/6466))
- [CT-1694] Deprecate event tracking tests ([#6467](https://github.com/dbt-labs/dbt-core/issues/6467))
- Reorganize structured logging events to have two top keys ([#6311](https://github.com/dbt-labs/dbt-core/issues/6311))
- Combine some logging events ([#1716](https://github.com/dbt-labs/dbt-core/issues/1716), [#1717](https://github.com/dbt-labs/dbt-core/issues/1717), [#1719](https://github.com/dbt-labs/dbt-core/issues/1719))
- Check length of escaped strings in the adapter test ([#6566](https://github.com/dbt-labs/dbt-core/issues/6566))
### 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 ([#6375](https://github.com/dbt-labs/dbt-core/pull/6375))
- Update agate requirement from <1.6.4,>=1.6 to >=1.6,<1.7.1 in /core ([#6506](https://github.com/dbt-labs/dbt-core/pull/6506))
### Contributors
- [@NiallRees](https://github.com/NiallRees) ([#5859](https://github.com/dbt-labs/dbt-core/issues/5859))
- [@agpapa](https://github.com/agpapa) ([#6365](https://github.com/dbt-labs/dbt-core/issues/6365))
- [@andy-clapson](https://github.com/andy-clapson) ([dbt-docs/#5791](https://github.com/dbt-labs/dbt-docs/issues/5791))
- [@callum-mcdata](https://github.com/callum-mcdata) ([#6398](https://github.com/dbt-labs/dbt-core/issues/6398), [#6507](https://github.com/dbt-labs/dbt-core/issues/6507))
- [@chamini2](https://github.com/chamini2) ([#6041](https://github.com/dbt-labs/dbt-core/issues/6041))
- [@daniel-murray](https://github.com/daniel-murray) ([#5016](https://github.com/dbt-labs/dbt-core/issues/5016))
- [@dave-connors-3](https://github.com/dave-connors-3) ([#5680](https://github.com/dbt-labs/dbt-core/issues/5680), [#5990](https://github.com/dbt-labs/dbt-core/issues/5990), [#6625](https://github.com/dbt-labs/dbt-core/issues/6625))
- [@dbeatty10](https://github.com/dbeatty10) ([#6411](https://github.com/dbt-labs/dbt-core/issues/6411), [dbt-docs/#6368](https://github.com/dbt-labs/dbt-docs/issues/6368), [#6394](https://github.com/dbt-labs/dbt-core/issues/6394), [#6294](https://github.com/dbt-labs/dbt-core/issues/6294), [#6566](https://github.com/dbt-labs/dbt-core/issues/6566))
- [@devmessias](https://github.com/devmessias) ([#6309](https://github.com/dbt-labs/dbt-core/issues/6309))
- [@eltociear](https://github.com/eltociear) ([#4904](https://github.com/dbt-labs/dbt-core/issues/4904))
- [@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) ([#5016](https://github.com/dbt-labs/dbt-core/issues/5016))
- [@joshuataylor](https://github.com/joshuataylor) ([#6147](https://github.com/dbt-labs/dbt-core/issues/6147))
- [@justbldwn](https://github.com/justbldwn) ([#6241](https://github.com/dbt-labs/dbt-core/issues/6241), [#6245](https://github.com/dbt-labs/dbt-core/issues/6245), [#6269](https://github.com/dbt-labs/dbt-core/issues/6269))
- [@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))
- [@mivanicova](https://github.com/mivanicova) ([#6488](https://github.com/dbt-labs/dbt-core/issues/6488))
- [@nshuman1](https://github.com/nshuman1) ([dbt-docs/#6529](https://github.com/dbt-labs/dbt-docs/issues/6529))
- [@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))
- [@tmastny](https://github.com/tmastny) ([#6216](https://github.com/dbt-labs/dbt-core/issues/6216))

8
.changes/1.4.1.md Normal file
View File

@@ -0,0 +1,8 @@
## dbt-core 1.4.1 - January 26, 2023
### Fixes
- [Regression] exposure_content referenced incorrectly ([#6738](https://github.com/dbt-labs/dbt-core/issues/6738))
### Contributors
- [@Mathyoub](https://github.com/Mathyoub) ([#6738](https://github.com/dbt-labs/dbt-core/issues/6738))

20
.changes/1.4.2.md Normal file
View File

@@ -0,0 +1,20 @@
## dbt-core 1.4.2 - February 23, 2023
### Fixes
- Sort cli vars before hashing for partial parsing ([#6710](https://github.com/dbt-labs/dbt-core/issues/6710))
- Remove pin on packaging and stop using it for prerelease comparisons ([#6834](https://github.com/dbt-labs/dbt-core/issues/6834))
- Readd depends_on.macros to SeedNode, to support seeds with hooks calling macros ([#6806](https://github.com/dbt-labs/dbt-core/issues/6806))
- Fix regression of --quiet cli parameter behavior ([#6749](https://github.com/dbt-labs/dbt-core/issues/6749))
- Ensure results from hooks contain nodes when processing them ([#6796](https://github.com/dbt-labs/dbt-core/issues/6796))
- Always flush stdout after logging ([#6901](https://github.com/dbt-labs/dbt-core/issues/6901))
- Set relation_name in test nodes at compile time ([#6930](https://github.com/dbt-labs/dbt-core/issues/6930))
- Fix disabled definition in WritableManifest ([#6752](https://github.com/dbt-labs/dbt-core/issues/6752))
- Fix regression in logbook log output ([#7028](https://github.com/dbt-labs/dbt-core/issues/7028))
### Docs
- Fix JSON path to overview docs ([dbt-docs/#366](https://github.com/dbt-labs/dbt-docs/issues/366))
### Contributors
- [@halvorlu](https://github.com/halvorlu) ([#366](https://github.com/dbt-labs/dbt-core/issues/366))

5
.changes/1.4.3.md Normal file
View File

@@ -0,0 +1,5 @@
## dbt-core 1.4.3 - February 24, 2023
### Fixes
- Fix semver comparison logic by ensuring numeric values ([#7039](https://github.com/dbt-labs/dbt-core/issues/7039))

View File

@@ -1,7 +0,0 @@
kind: "Dependency"
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]
Issue: 4904
PR: 5917

View File

@@ -1,7 +0,0 @@
kind: "Dependency"
body: "Bump black from 22.8.0 to 22.10.0"
time: 2022-10-07T00:08:48.00000Z
custom:
Author: dependabot[bot]
Issue: 4904
PR: 6019

View File

@@ -1,7 +0,0 @@
kind: "Dependency"
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]
Issue: 4904
PR: 6108

View File

@@ -1,7 +0,0 @@
kind: "Dependency"
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]
Issue: 4904
PR: 6144

View File

@@ -1,7 +0,0 @@
kind: Docs
body: minor doc correction
time: 2022-09-08T15:41:57.689162-04:00
custom:
Author: andy-clapson
Issue: "5791"
PR: "5684"

View File

@@ -1,7 +0,0 @@
kind: Docs
body: Generate API docs for new CLI interface
time: 2022-10-07T09:06:56.446078-05:00
custom:
Author: stu-k
Issue: "5528"
PR: "6022"

View File

@@ -1,6 +0,0 @@
kind: Docs
time: 2022-10-17T17:14:11.715348-05:00
custom:
Author: paulbenschmidt
Issue: "5880"
PR: "324"

View File

@@ -1,7 +0,0 @@
kind: Docs
body: Fix rendering of sample code for metrics
time: 2022-11-16T15:57:43.204201+01:00
custom:
Author: jtcohen6
Issue: "323"
PR: "346"

View File

@@ -1,8 +0,0 @@
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"
PR: "5859"

View File

@@ -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"

View File

@@ -1,7 +0,0 @@
kind: Features
body: Friendlier error messages when packages.yml is malformed
time: 2022-09-12T12:59:35.121188+01:00
custom:
Author: jared-rimmer
Issue: "5486"
PR: "5812"

View File

@@ -1,7 +0,0 @@
kind: Features
body: Migrate dbt-utils current_timestamp macros into core + adapters
time: 2022-09-14T09:56:25.97818-07:00
custom:
Author: colin-rogers-dbt
Issue: "5521"
PR: "5838"

View File

@@ -1,7 +0,0 @@
kind: Features
body: Allow partitions in external tables to be supplied as a list
time: 2022-09-25T21:16:51.051239654+02:00
custom:
Author: pgoslatara
Issue: "5929"
PR: "5930"

View File

@@ -1,7 +0,0 @@
kind: Features
body: extend -f flag shorthand for seed command
time: 2022-10-03T11:07:05.381632-05:00
custom:
Author: dave-connors-3
Issue: "5990"
PR: "5991"

View File

@@ -1,8 +0,0 @@
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"
PR: "6202"

View File

@@ -1,7 +0,0 @@
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"
PR: "6247"

View File

@@ -1,7 +0,0 @@
kind: Features
body: Exposures support metrics in lineage
time: 2022-11-30T11:29:13.256034-05:00
custom:
Author: michelleark
Issue: "6057"
PR: "6342"

View File

@@ -1,7 +0,0 @@
kind: Fixes
body: Account for disabled flags on models in schema files more completely
time: 2022-09-16T10:48:54.162273-05:00
custom:
Author: emmyoop
Issue: "3992"
PR: "5868"

View File

@@ -1,7 +0,0 @@
kind: Fixes
body: Add validation of enabled config for metrics, exposures and sources
time: 2022-10-10T11:32:18.752322-05:00
custom:
Author: emmyoop
Issue: "6030"
PR: "6038"

View File

@@ -1,7 +0,0 @@
kind: Fixes
body: check length of args of python model function before accessing it
time: 2022-10-11T16:07:15.464093-04:00
custom:
Author: chamini2
Issue: "6041"
PR: "6042"

View File

@@ -1,8 +0,0 @@
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"
PR: "5874"

View File

@@ -1,7 +0,0 @@
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"
PR: "6059"

View File

@@ -1,7 +0,0 @@
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"
PR: "6251"

View File

@@ -1,7 +0,0 @@
kind: Under the Hood
body: Put black config in explicit config
time: 2022-09-27T19:42:59.241433-07:00
custom:
Author: max-sixty
Issue: "5946"
PR: "5947"

View File

@@ -1,7 +0,0 @@
kind: Under the Hood
body: Added flat_graph attribute the Manifest class's deepcopy() coverage
time: 2022-09-29T13:44:06.275941-04:00
custom:
Author: peterallenwebb
Issue: "5809"
PR: "5975"

View File

@@ -1,7 +0,0 @@
kind: Under the Hood
body: Add mypy configs so `mypy` passes from CLI
time: 2022-10-05T12:03:10.061263-07:00
custom:
Author: max-sixty
Issue: "5983"
PR: "5983"

View File

@@ -1,7 +0,0 @@
kind: Under the Hood
body: Exception message cleanup.
time: 2022-10-07T09:46:27.682872-05:00
custom:
Author: emmyoop
Issue: "6023"
PR: "6024"

View File

@@ -1,7 +0,0 @@
kind: Under the Hood
body: Add dmypy cache to gitignore
time: 2022-10-07T14:00:44.227644-07:00
custom:
Author: max-sixty
Issue: "6028"
PR: "5978"

View File

@@ -1,7 +0,0 @@
kind: Under the Hood
body: Provide useful errors when the value of 'materialized' is invalid
time: 2022-10-13T18:19:12.167548-04:00
custom:
Author: peterallenwebb
Issue: "5229"
PR: "6025"

View File

@@ -1,7 +0,0 @@
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"
PR: "6086"

View File

@@ -1,7 +0,0 @@
kind: Under the Hood
body: Clean up string formatting
time: 2022-10-17T15:58:44.676549-04:00
custom:
Author: eve-johns
Issue: "6068"
PR: "6082"

View File

@@ -1,7 +0,0 @@
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"
PR: "6172"

View File

@@ -1,7 +0,0 @@
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"
PR: "6174"

View File

@@ -1,7 +0,0 @@
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"
PR: "6226"

View File

@@ -1,7 +0,0 @@
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"
PR: "6228"

View File

@@ -1,7 +0,0 @@
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"
PR: "6230"

View File

@@ -1,7 +0,0 @@
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"
PR: "6187"

View File

@@ -6,19 +6,67 @@ changelogPath: CHANGELOG.md
versionExt: md
versionFormat: '## dbt-core {{.Version}} - {{.Time.Format "January 02, 2006"}}'
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:
- label: Breaking Changes
- label: Features
- label: Fixes
- 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: 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
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:
afterChangelogHeader: 1
@@ -33,42 +81,47 @@ custom:
type: string
minLength: 3
- key: Issue
label: GitHub Issue Number
type: int
minInt: 1
- key: PR
label: GitHub Pull Request Number
type: int
minInt: 1
label: GitHub Issue Number (separated by a single space if multiple)
type: string
minLength: 1
footerFormat: |
{{- $contributorDict := dict }}
{{- /* 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 }}
{{- $authorList := splitList " " $change.Custom.Author }}
{{- /* loop through all authors for a PR */}}
{{- /* loop through all authors for a single changelog */}}
{{- range $author := $authorList }}
{{- $authorLower := lower $author }}
{{- /* we only want to include non-core team contributors */}}
{{- if not (has $authorLower $core_team)}}
{{- /* Docs kind link back to dbt-docs instead of dbt-core PRs */}}
{{- $prLink := $change.Kind }}
{{- if eq $change.Kind "Docs" }}
{{- $prLink = "[dbt-docs/#pr](https://github.com/dbt-labs/dbt-docs/pull/pr)" | replace "pr" $change.Custom.PR }}
{{- else }}
{{- $prLink = "[#pr](https://github.com/dbt-labs/dbt-core/pull/pr)" | replace "pr" $change.Custom.PR }}
{{- end }}
{{- /* check if this contributor has other PRs associated with them already */}}
{{- if hasKey $contributorDict $author }}
{{- $prList := get $contributorDict $author }}
{{- $prList = append $prList $prLink }}
{{- $contributorDict := set $contributorDict $author $prList }}
{{- else }}
{{- $prList := list $prLink }}
{{- $contributorDict := set $contributorDict $author $prList }}
{{- end }}
{{- end}}
{{- $changeList := splitList " " $change.Custom.Author }}
{{- $IssueList := list }}
{{- $changeLink := $change.Kind }}
{{- if or (eq $change.Kind "Dependencies") (eq $change.Kind "Security") }}
{{- $changes := splitList " " $change.Custom.PR }}
{{- range $issueNbr := $changes }}
{{- $changeLink := "[#nbr](https://github.com/dbt-labs/dbt-core/pull/nbr)" | replace "nbr" $issueNbr }}
{{- $IssueList = append $IssueList $changeLink }}
{{- end -}}
{{- else }}
{{- $changes := splitList " " $change.Custom.Issue }}
{{- range $issueNbr := $changes }}
{{- $changeLink := "[#nbr](https://github.com/dbt-labs/dbt-core/issues/nbr)" | replace "nbr" $issueNbr }}
{{- $IssueList = append $IssueList $changeLink }}
{{- end -}}
{{- end }}
{{- /* check if this contributor has other changes associated with them already */}}
{{- if hasKey $contributorDict $author }}
{{- $contributionList := get $contributorDict $author }}
{{- $contributionList = concat $contributionList $IssueList }}
{{- $contributorDict := set $contributorDict $author $contributionList }}
{{- else }}
{{- $contributionList := $IssueList }}
{{- $contributorDict := set $contributorDict $author $contributionList }}
{{- end }}
{{- end}}
{{- end}}
{{- end }}
{{- /* no indentation here for formatting so the final markdown doesn't have unneeded indentations */}}

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
core/dbt/include/index.html binary
tests/functional/artifacts/data/state/*/manifest.json binary

View File

@@ -40,7 +40,7 @@ jobs:
matrix:
include:
- label: "dependencies"
changie_kind: "Dependency"
changie_kind: "Dependencies"
- label: "snyk"
changie_kind: "Security"
runs-on: ubuntu-latest
@@ -58,4 +58,4 @@ jobs:
commit_message: "Add automated changelog yaml from template for bot PR"
changie_kind: ${{ matrix.changie_kind }}
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 }}"

View File

@@ -34,6 +34,7 @@ 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 }}
@@ -44,8 +45,6 @@ jobs:
echo "env.CLI_DIR: ${{ env.CLI_DIR }}"
echo "env.DOCS_BUILD_DIR: ${{ env.DOCS_BUILD_DIR }}"
echo "env.DOCS_DIR: ${{ env.DOCS_DIR }}"
echo ">>>>> git log"
git log --pretty=oneline | head -5
- name: git checkout
uses: actions/checkout@v3

View File

@@ -73,7 +73,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10"]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
env:
TOXENV: "unit"
@@ -118,7 +118,7 @@ jobs:
strategy:
fail-fast: false
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]
include:
- python-version: 3.8

109
.github/workflows/nightly-release.yml vendored Normal file
View File

@@ -0,0 +1,109 @@
# **what?**
# Nightly releases to GitHub and PyPI. This workflow produces the following outcome:
# - generate and validate data for night release (commit SHA, version number, release branch);
# - pass data to release workflow;
# - night release will be pushed to GitHub as a draft release;
# - night build will be pushed to test PyPI;
#
# **why?**
# Ensure an automated and tested release process for nightly builds
#
# **when?**
# This workflow runs on schedule or can be run manually on demand.
name: Nightly Test Release to GitHub and PyPI
on:
workflow_dispatch: # for manual triggering
schedule:
- cron: 0 9 * * *
permissions:
contents: write # this is the permission that allows creating a new release
defaults:
run:
shell: bash
env:
RELEASE_BRANCH: "main"
jobs:
aggregate-release-data:
runs-on: ubuntu-latest
outputs:
commit_sha: ${{ steps.resolve-commit-sha.outputs.release_commit }}
version_number: ${{ steps.nightly-release-version.outputs.number }}
release_branch: ${{ steps.release-branch.outputs.name }}
steps:
- name: "Checkout ${{ github.repository }} Branch ${{ env.RELEASE_BRANCH }}"
uses: actions/checkout@v3
with:
ref: ${{ env.RELEASE_BRANCH }}
- name: "Resolve Commit To Release"
id: resolve-commit-sha
run: |
commit_sha=$(git rev-parse HEAD)
echo "release_commit=$commit_sha" >> $GITHUB_OUTPUT
- name: "Get Current Version Number"
id: version-number-sources
run: |
current_version=`awk -F"current_version = " '{print $2}' .bumpversion.cfg | tr '\n' ' '`
echo "current_version=$current_version" >> $GITHUB_OUTPUT
- name: "Audit Version And Parse Into Parts"
id: semver
uses: dbt-labs/actions/parse-semver@v1.1.0
with:
version: ${{ steps.version-number-sources.outputs.current_version }}
- name: "Get Current Date"
id: current-date
run: echo "date=$(date +'%m%d%Y')" >> $GITHUB_OUTPUT
- name: "Generate Nightly Release Version Number"
id: nightly-release-version
run: |
number="${{ steps.semver.outputs.version }}.dev${{ steps.current-date.outputs.date }}+nightly"
echo "number=$number" >> $GITHUB_OUTPUT
- name: "Audit Nightly Release Version And Parse Into Parts"
uses: dbt-labs/actions/parse-semver@v1.1.0
with:
version: ${{ steps.nightly-release-version.outputs.number }}
- name: "Set Release Branch"
id: release-branch
run: |
echo "name=${{ env.RELEASE_BRANCH }}" >> $GITHUB_OUTPUT
log-outputs-aggregate-release-data:
runs-on: ubuntu-latest
needs: [aggregate-release-data]
steps:
- name: "[DEBUG] Log Outputs"
run: |
echo commit_sha : ${{ needs.aggregate-release-data.outputs.commit_sha }}
echo version_number: ${{ needs.aggregate-release-data.outputs.version_number }}
echo release_branch: ${{ needs.aggregate-release-data.outputs.release_branch }}
release-github-pypi:
needs: [aggregate-release-data]
uses: ./.github/workflows/release.yml
with:
sha: ${{ needs.aggregate-release-data.outputs.commit_sha }}
target_branch: ${{ needs.aggregate-release-data.outputs.release-branch }}
version_number: ${{ needs.aggregate-release-data.outputs.version_number }}
build_script_path: "scripts/build-dist.sh"
env_setup_script_path: "scripts/env-setup.sh"
s3_bucket_name: "core-team-artifacts"
package_test_command: "dbt --version"
test_run: true
nightly_release: true
secrets: inherit

View File

@@ -1,24 +1,110 @@
# **what?**
# Take the given commit, run unit tests specifically on that sha, build and
# package it, and then release to GitHub and PyPi with that specific build
# Release workflow provides the following steps:
# - checkout the given commit;
# - validate version in sources and changelog file for given version;
# - bump the version and generate a changelog if needed;
# - merge all changes to the target branch if needed;
# - run unit and integration tests against given commit;
# - build and package that SHA;
# - release it to GitHub and PyPI with that specific build;
#
# **why?**
# Ensure an automated and tested release process
#
# **when?**
# This will only run manually with a given sha and version
# This workflow can be run manually on demand or can be called by other workflows
name: Release to GitHub and PyPi
name: Release to GitHub and PyPI
on:
workflow_dispatch:
inputs:
sha:
description: 'The last commit sha in the release'
required: true
description: "The last commit sha in the release"
type: string
required: true
target_branch:
description: "The branch to release from"
type: string
required: true
version_number:
description: 'The release version number (i.e. 1.0.0b1)'
required: true
description: "The release version number (i.e. 1.0.0b1)"
type: string
required: true
build_script_path:
description: "Build script path"
type: string
default: "scripts/build-dist.sh"
required: true
env_setup_script_path:
description: "Environment setup script path"
type: string
default: "scripts/env-setup.sh"
required: false
s3_bucket_name:
description: "AWS S3 bucket name"
type: string
default: "core-team-artifacts"
required: true
package_test_command:
description: "Package test command"
type: string
default: "dbt --version"
required: true
test_run:
description: "Test run (Publish release as draft)"
type: boolean
default: true
required: false
nightly_release:
description: "Nightly release to dev environment"
type: boolean
default: false
required: false
workflow_call:
inputs:
sha:
description: "The last commit sha in the release"
type: string
required: true
target_branch:
description: "The branch to release from"
type: string
required: true
version_number:
description: "The release version number (i.e. 1.0.0b1)"
type: string
required: true
build_script_path:
description: "Build script path"
type: string
default: "scripts/build-dist.sh"
required: true
env_setup_script_path:
description: "Environment setup script path"
type: string
default: "scripts/env-setup.sh"
required: false
s3_bucket_name:
description: "AWS S3 bucket name"
type: string
default: "core-team-artifacts"
required: true
package_test_command:
description: "Package test command"
type: string
default: "dbt --version"
required: true
test_run:
description: "Test run (Publish release as draft)"
type: boolean
default: true
required: false
nightly_release:
description: "Nightly release to dev environment"
type: boolean
default: false
required: false
permissions:
contents: write # this is the permission that allows creating a new release
@@ -28,175 +114,116 @@ defaults:
shell: bash
jobs:
unit:
name: Unit test
log-inputs:
name: Log Inputs
runs-on: ubuntu-latest
env:
TOXENV: "unit"
steps:
- name: Check out the repository
uses: actions/checkout@v2
with:
persist-credentials: false
ref: ${{ github.event.inputs.sha }}
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install python dependencies
- name: "[DEBUG] Print Variables"
run: |
pip install --user --upgrade pip
pip install tox
pip --version
tox --version
echo The last commit sha in the release: ${{ inputs.sha }}
echo The branch to release from: ${{ inputs.target_branch }}
echo The release version number: ${{ inputs.version_number }}
echo Build script path: ${{ inputs.build_script_path }}
echo Environment setup script path: ${{ inputs.env_setup_script_path }}
echo AWS S3 bucket name: ${{ inputs.s3_bucket_name }}
echo Package test command: ${{ inputs.package_test_command }}
echo Test run: ${{ inputs.test_run }}
echo Nightly release: ${{ inputs.nightly_release }}
- name: Run tox
run: tox
bump-version-generate-changelog:
name: Bump package version, Generate changelog
build:
name: build packages
uses: dbt-labs/dbt-release/.github/workflows/release-prep.yml@main
with:
sha: ${{ inputs.sha }}
version_number: ${{ inputs.version_number }}
target_branch: ${{ inputs.target_branch }}
env_setup_script_path: ${{ inputs.env_setup_script_path }}
test_run: ${{ inputs.test_run }}
nightly_release: ${{ inputs.nightly_release }}
secrets: inherit
log-outputs-bump-version-generate-changelog:
name: "[Log output] Bump package version, Generate changelog"
if: ${{ !failure() && !cancelled() }}
needs: [bump-version-generate-changelog]
runs-on: ubuntu-latest
steps:
- name: Check out the repository
uses: actions/checkout@v2
with:
persist-credentials: false
ref: ${{ github.event.inputs.sha }}
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install python dependencies
- name: Print variables
run: |
pip install --user --upgrade pip
pip install --upgrade setuptools wheel twine check-wheel-contents
pip --version
echo Final SHA : ${{ needs.bump-version-generate-changelog.outputs.final_sha }}
echo Changelog path: ${{ needs.bump-version-generate-changelog.outputs.changelog_path }}
- name: Build distributions
run: ./scripts/build-dist.sh
build-test-package:
name: Build, Test, Package
if: ${{ !failure() && !cancelled() }}
needs: [bump-version-generate-changelog]
- name: Show distributions
run: ls -lh dist/
uses: dbt-labs/dbt-release/.github/workflows/build.yml@main
- name: Check distribution descriptions
run: |
twine check dist/*
with:
sha: ${{ needs.bump-version-generate-changelog.outputs.final_sha }}
version_number: ${{ inputs.version_number }}
changelog_path: ${{ needs.bump-version-generate-changelog.outputs.changelog_path }}
build_script_path: ${{ inputs.build_script_path }}
s3_bucket_name: ${{ inputs.s3_bucket_name }}
package_test_command: ${{ inputs.package_test_command }}
test_run: ${{ inputs.test_run }}
nightly_release: ${{ inputs.nightly_release }}
- name: Check wheel contents
run: |
check-wheel-contents dist/*.whl --ignore W007,W008
- uses: actions/upload-artifact@v2
with:
name: dist
path: |
dist/
!dist/dbt-${{github.event.inputs.version_number}}.tar.gz
test-build:
name: verify packages
needs: [build, unit]
runs-on: ubuntu-latest
steps:
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install python dependencies
run: |
pip install --user --upgrade pip
pip install --upgrade wheel
pip --version
- uses: actions/download-artifact@v2
with:
name: dist
path: dist/
- name: Show distributions
run: ls -lh dist/
- name: Install wheel distributions
run: |
find ./dist/*.whl -maxdepth 1 -type f | xargs pip install --force-reinstall --find-links=dist/
- name: Check wheel distributions
run: |
dbt --version
- name: Install source distributions
run: |
find ./dist/*.gz -maxdepth 1 -type f | xargs pip install --force-reinstall --find-links=dist/
- name: Check source distributions
run: |
dbt --version
secrets:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
github-release:
name: GitHub Release
if: ${{ !failure() && !cancelled() }}
needs: test-build
needs: [bump-version-generate-changelog, build-test-package]
runs-on: ubuntu-latest
uses: dbt-labs/dbt-release/.github/workflows/github-release.yml@main
steps:
- uses: actions/download-artifact@v2
with:
name: dist
path: '.'
# Need to set an output variable because env variables can't be taken as input
# This is needed for the next step with releasing to GitHub
- name: Find release type
id: release_type
env:
IS_PRERELEASE: ${{ contains(github.event.inputs.version_number, 'rc') || contains(github.event.inputs.version_number, 'b') }}
run: |
echo ::set-output name=isPrerelease::$IS_PRERELEASE
- name: Creating GitHub Release
uses: softprops/action-gh-release@v1
with:
name: dbt-core v${{github.event.inputs.version_number}}
tag_name: v${{github.event.inputs.version_number}}
prerelease: ${{ steps.release_type.outputs.isPrerelease }}
target_commitish: ${{github.event.inputs.sha}}
body: |
[Release notes](https://github.com/dbt-labs/dbt-core/blob/main/CHANGELOG.md)
files: |
dbt_postgres-${{github.event.inputs.version_number}}-py3-none-any.whl
dbt_core-${{github.event.inputs.version_number}}-py3-none-any.whl
dbt-postgres-${{github.event.inputs.version_number}}.tar.gz
dbt-core-${{github.event.inputs.version_number}}.tar.gz
with:
sha: ${{ needs.bump-version-generate-changelog.outputs.final_sha }}
version_number: ${{ inputs.version_number }}
changelog_path: ${{ needs.bump-version-generate-changelog.outputs.changelog_path }}
test_run: ${{ inputs.test_run }}
pypi-release:
name: Pypi release
name: PyPI Release
runs-on: ubuntu-latest
needs: [github-release]
needs: github-release
uses: dbt-labs/dbt-release/.github/workflows/pypi-release.yml@main
environment: PypiProd
steps:
- uses: actions/download-artifact@v2
with:
name: dist
path: 'dist'
with:
version_number: ${{ inputs.version_number }}
test_run: ${{ inputs.test_run }}
- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@v1.4.2
with:
password: ${{ secrets.PYPI_API_TOKEN }}
secrets:
PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
TEST_PYPI_API_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }}
slack-notification:
name: Slack Notification
if: ${{ failure() && (!inputs.test_run || inputs.nightly_release) }}
needs:
[
bump-version-generate-changelog,
build-test-package,
github-release,
pypi-release,
]
uses: dbt-labs/dbt-release/.github/workflows/slack-post-notification.yml@main
with:
status: "failure"
secrets:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_DEV_CORE_ALERTS }}

View File

@@ -5,6 +5,170 @@
- "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)
## dbt-core 1.4.3 - February 24, 2023
### Fixes
- Fix semver comparison logic by ensuring numeric values ([#7039](https://github.com/dbt-labs/dbt-core/issues/7039))
## dbt-core 1.4.2 - February 23, 2023
### Fixes
- Sort cli vars before hashing for partial parsing ([#6710](https://github.com/dbt-labs/dbt-core/issues/6710))
- Remove pin on packaging and stop using it for prerelease comparisons ([#6834](https://github.com/dbt-labs/dbt-core/issues/6834))
- Readd depends_on.macros to SeedNode, to support seeds with hooks calling macros ([#6806](https://github.com/dbt-labs/dbt-core/issues/6806))
- Fix regression of --quiet cli parameter behavior ([#6749](https://github.com/dbt-labs/dbt-core/issues/6749))
- Ensure results from hooks contain nodes when processing them ([#6796](https://github.com/dbt-labs/dbt-core/issues/6796))
- Always flush stdout after logging ([#6901](https://github.com/dbt-labs/dbt-core/issues/6901))
- Set relation_name in test nodes at compile time ([#6930](https://github.com/dbt-labs/dbt-core/issues/6930))
- Fix disabled definition in WritableManifest ([#6752](https://github.com/dbt-labs/dbt-core/issues/6752))
- Fix regression in logbook log output ([#7028](https://github.com/dbt-labs/dbt-core/issues/7028))
### Docs
- Fix JSON path to overview docs ([dbt-docs/#366](https://github.com/dbt-labs/dbt-docs/issues/366))
### Contributors
- [@halvorlu](https://github.com/halvorlu) ([#366](https://github.com/dbt-labs/dbt-core/issues/366))
## dbt-core 1.4.1 - January 26, 2023
### Fixes
- [Regression] exposure_content referenced incorrectly ([#6738](https://github.com/dbt-labs/dbt-core/issues/6738))
### Contributors
- [@Mathyoub](https://github.com/Mathyoub) ([#6738](https://github.com/dbt-labs/dbt-core/issues/6738))
## dbt-core 1.4.0 - January 25, 2023
### Breaking Changes
- Cleaned up exceptions to directly raise in code. Also updated the existing exception to meet PEP guidelines.Removed use of all exception functions in the code base and marked them all as deprecated to be removed next minor release. ([#6339](https://github.com/dbt-labs/dbt-core/issues/6339), [#6393](https://github.com/dbt-labs/dbt-core/issues/6393), [#6460](https://github.com/dbt-labs/dbt-core/issues/6460))
### Features
- Added favor-state flag to optionally favor state nodes even if unselected node exists ([#5016](https://github.com/dbt-labs/dbt-core/issues/5016))
- 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))
- incremental predicates ([#5680](https://github.com/dbt-labs/dbt-core/issues/5680))
- Friendlier error messages when packages.yml is malformed ([#5486](https://github.com/dbt-labs/dbt-core/issues/5486))
- 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))
- Making timestamp optional for metrics ([#6398](https://github.com/dbt-labs/dbt-core/issues/6398))
- The meta configuration field is now included in the node_info property of structured logs. ([#6216](https://github.com/dbt-labs/dbt-core/issues/6216))
- Adds buildable selection mode ([#6365](https://github.com/dbt-labs/dbt-core/issues/6365))
- --warn-error-options: Treat warnings as errors for specific events, based on user configuration ([#6165](https://github.com/dbt-labs/dbt-core/issues/6165))
### 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))
- fix missing f-strings, convert old .format() messages to f-strings for consistency ([#6241](https://github.com/dbt-labs/dbt-core/issues/6241))
- Clarify Error Message for how many models are allowed in a Python file ([#6245](https://github.com/dbt-labs/dbt-core/issues/6245))
- Fix typo in util.py ([#4904](https://github.com/dbt-labs/dbt-core/issues/4904))
- 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))
- add pre-commit install to make dev script in Makefile ([#6269](https://github.com/dbt-labs/dbt-core/issues/6269))
- Late-rendering for `pre_` and `post_hook`s in `dbt_project.yml` ([#6411](https://github.com/dbt-labs/dbt-core/issues/6411))
- [CT-1284] Change Python model default materialization to table ([#5989](https://github.com/dbt-labs/dbt-core/issues/5989))
- [CT-1591] Don't parse empty Python files ([#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))
- fix docs generate --defer by adding defer_to_manifest to before_run ([#6488](https://github.com/dbt-labs/dbt-core/issues/6488))
- Bug when partial parsing with an empty schema file ([#4850](https://github.com/dbt-labs/dbt-core/issues/4850))
- Fix DBT_FAVOR_STATE env var ([#5859](https://github.com/dbt-labs/dbt-core/issues/5859))
- Restore historical behavior of certain disabled test messages, so that they are at the less obtrusive debug level, rather than the warning level. ([#6501](https://github.com/dbt-labs/dbt-core/issues/6501))
- Bump mashumuro version to get regression fix and add unit test to verify that fix. ([#6428](https://github.com/dbt-labs/dbt-core/issues/6428))
- Call update_event_status earlier for node results. Rename event 'HookFinished' -> FinishedRunningStats ([#6571](https://github.com/dbt-labs/dbt-core/issues/6571))
- Provide backward compatibility for `get_merge_sql` arguments ([#6625](https://github.com/dbt-labs/dbt-core/issues/6625))
- Fix behavior of --favor-state with --defer ([#6617](https://github.com/dbt-labs/dbt-core/issues/6617))
- Include adapter_response in NodeFinished run_result log event ([#6703](https://github.com/dbt-labs/dbt-core/issues/6703))
### 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))
- Updated minor typos encountered when skipping profile setup ([dbt-docs/#6529](https://github.com/dbt-labs/dbt-docs/issues/6529))
### 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))
- Treat dense text blobs as binary for `git grep` ([#6294](https://github.com/dbt-labs/dbt-core/issues/6294))
- Prune partial parsing logging events ([#6313](https://github.com/dbt-labs/dbt-core/issues/6313))
- Updating the deprecation warning in the metric attributes renamed event ([#6507](https://github.com/dbt-labs/dbt-core/issues/6507))
- [CT-1693] Port severity test to Pytest ([#6466](https://github.com/dbt-labs/dbt-core/issues/6466))
- [CT-1694] Deprecate event tracking tests ([#6467](https://github.com/dbt-labs/dbt-core/issues/6467))
- Reorganize structured logging events to have two top keys ([#6311](https://github.com/dbt-labs/dbt-core/issues/6311))
- Combine some logging events ([#1716](https://github.com/dbt-labs/dbt-core/issues/1716), [#1717](https://github.com/dbt-labs/dbt-core/issues/1717), [#1719](https://github.com/dbt-labs/dbt-core/issues/1719))
- Check length of escaped strings in the adapter test ([#6566](https://github.com/dbt-labs/dbt-core/issues/6566))
### 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 ([#6375](https://github.com/dbt-labs/dbt-core/pull/6375))
- Update agate requirement from <1.6.4,>=1.6 to >=1.6,<1.7.1 in /core ([#6506](https://github.com/dbt-labs/dbt-core/pull/6506))
### Contributors
- [@NiallRees](https://github.com/NiallRees) ([#5859](https://github.com/dbt-labs/dbt-core/issues/5859))
- [@agpapa](https://github.com/agpapa) ([#6365](https://github.com/dbt-labs/dbt-core/issues/6365))
- [@andy-clapson](https://github.com/andy-clapson) ([dbt-docs/#5791](https://github.com/dbt-labs/dbt-docs/issues/5791))
- [@callum-mcdata](https://github.com/callum-mcdata) ([#6398](https://github.com/dbt-labs/dbt-core/issues/6398), [#6507](https://github.com/dbt-labs/dbt-core/issues/6507))
- [@chamini2](https://github.com/chamini2) ([#6041](https://github.com/dbt-labs/dbt-core/issues/6041))
- [@daniel-murray](https://github.com/daniel-murray) ([#5016](https://github.com/dbt-labs/dbt-core/issues/5016))
- [@dave-connors-3](https://github.com/dave-connors-3) ([#5680](https://github.com/dbt-labs/dbt-core/issues/5680), [#5990](https://github.com/dbt-labs/dbt-core/issues/5990), [#6625](https://github.com/dbt-labs/dbt-core/issues/6625))
- [@dbeatty10](https://github.com/dbeatty10) ([#6411](https://github.com/dbt-labs/dbt-core/issues/6411), [dbt-docs/#6368](https://github.com/dbt-labs/dbt-docs/issues/6368), [#6394](https://github.com/dbt-labs/dbt-core/issues/6394), [#6294](https://github.com/dbt-labs/dbt-core/issues/6294), [#6566](https://github.com/dbt-labs/dbt-core/issues/6566))
- [@devmessias](https://github.com/devmessias) ([#6309](https://github.com/dbt-labs/dbt-core/issues/6309))
- [@eltociear](https://github.com/eltociear) ([#4904](https://github.com/dbt-labs/dbt-core/issues/4904))
- [@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) ([#5016](https://github.com/dbt-labs/dbt-core/issues/5016))
- [@joshuataylor](https://github.com/joshuataylor) ([#6147](https://github.com/dbt-labs/dbt-core/issues/6147))
- [@justbldwn](https://github.com/justbldwn) ([#6241](https://github.com/dbt-labs/dbt-core/issues/6241), [#6245](https://github.com/dbt-labs/dbt-core/issues/6245), [#6269](https://github.com/dbt-labs/dbt-core/issues/6269))
- [@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))
- [@mivanicova](https://github.com/mivanicova) ([#6488](https://github.com/dbt-labs/dbt-core/issues/6488))
- [@nshuman1](https://github.com/nshuman1) ([dbt-docs/#6529](https://github.com/dbt-labs/dbt-docs/issues/6529))
- [@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))
- [@tmastny](https://github.com/tmastny) ([#6216](https://github.com/dbt-labs/dbt-core/issues/6216))
## Previous Releases

View File

@@ -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:
- [`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
- [`flake8`](https://flake8.pycqa.org/en/latest/) for code linting
- [`black`](https://github.com/psf/black) for code formatting
@@ -96,12 +96,15 @@ brew install postgresql
### Installation
First make sure that you set up your `virtualenv` as described in [Setting up an environment](#setting-up-an-environment). Also ensure you have the latest version of pip installed with `pip install --upgrade pip`. Next, install `dbt-core` (and its dependencies) with:
First make sure that you set up your `virtualenv` as described in [Setting up an environment](#setting-up-an-environment). Also ensure you have the latest version of pip installed with `pip install --upgrade pip`. Next, install `dbt-core` (and its dependencies):
```sh
make dev
# or
```
or, alternatively:
```sh
pip install -r dev-requirements.txt -r editable-requirements.txt
pre-commit install
```
When installed in this way, any changes you make to your local copy of the source code will be reflected immediately in your next `dbt` run.
@@ -160,7 +163,7 @@ suites.
#### `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`
@@ -201,13 +204,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.
* 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.
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`.

View File

@@ -49,6 +49,9 @@ RUN apt-get update \
python3.10 \
python3.10-dev \
python3.10-venv \
python3.11 \
python3.11-dev \
python3.11-venv \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

View File

@@ -19,11 +19,16 @@ CI_FLAGS =\
LOG_DIR=./logs\
DBT_LOG_FORMAT=json
.PHONY: dev
dev: ## Installs dbt-* packages in develop mode along with development dependencies.
.PHONY: dev_req
dev_req: ## Installs dbt-* packages in develop mode along with only development dependencies.
@\
pip install -r dev-requirements.txt -r editable-requirements.txt
.PHONY: dev
dev: dev_req ## Installs dbt-* packages in develop mode along with development dependencies and pre-commit.
@\
pre-commit install
.PHONY: mypy
mypy: .env ## Runs mypy against staged changes for static type checking.
@\

View File

@@ -2,50 +2,59 @@
## 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
### constants.py
### dataclass_schema.py
### deprecations.py
### exceptions.py
### flags.py
### helper_types.py
### hooks.py
### lib.py
### links.py
### logger.py
### main.py
### node_types.py
### profiler.py
### selected_resources.py
### semver.py
### tracking.py
### ui.py
### utils.py
### version.py
## The subdirectories will be documented in a README in the subdirectory
* config
* include
* adapters
* context
* deps
* graph
* task
* cli
* clients
* config
* context
* contracts
* deps
* docs
* events
* graph
* include
* parser
* task
* tests

View File

@@ -2,7 +2,7 @@ from dataclasses import dataclass
import re
from typing import Dict, ClassVar, Any, Optional
from dbt.exceptions import RuntimeException
from dbt.exceptions import DbtRuntimeError
@dataclass
@@ -85,7 +85,7 @@ class Column:
def string_size(self) -> int:
if not self.is_string():
raise RuntimeException("Called string_size() on non-string field!")
raise DbtRuntimeError("Called string_size() on non-string field!")
if self.dtype == "text" or self.char_size is None:
# char_size should never be None. Handle it reasonably just in case
@@ -124,7 +124,7 @@ class Column:
def from_description(cls, name: str, raw_data_type: str) -> "Column":
match = re.match(r"([^(]+)(\([^)]+\))?", raw_data_type)
if match is None:
raise RuntimeException(f'Could not interpret data type "{raw_data_type}"')
raise DbtRuntimeError(f'Could not interpret data type "{raw_data_type}"')
data_type, size_info = match.groups()
char_size = None
numeric_precision = None
@@ -137,7 +137,7 @@ class Column:
try:
char_size = int(parts[0])
except ValueError:
raise RuntimeException(
raise DbtRuntimeError(
f'Could not interpret data_type "{raw_data_type}": '
f'could not convert "{parts[0]}" to an integer'
)
@@ -145,14 +145,14 @@ class Column:
try:
numeric_precision = int(parts[0])
except ValueError:
raise RuntimeException(
raise DbtRuntimeError(
f'Could not interpret data_type "{raw_data_type}": '
f'could not convert "{parts[0]}" to an integer'
)
try:
numeric_scale = int(parts[1])
except ValueError:
raise RuntimeException(
raise DbtRuntimeError(
f'Could not interpret data_type "{raw_data_type}": '
f'could not convert "{parts[1]}" to an integer'
)

View File

@@ -48,6 +48,7 @@ from dbt.events.types import (
Rollback,
RollbackFailed,
)
from dbt.events.contextvars import get_node_info
from dbt import flags
from dbt.utils import cast_to_str
@@ -90,13 +91,13 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
key = self.get_thread_identifier()
with self.lock:
if key not in self.thread_connections:
raise dbt.exceptions.InvalidConnectionException(key, list(self.thread_connections))
raise dbt.exceptions.InvalidConnectionError(key, list(self.thread_connections))
return self.thread_connections[key]
def set_thread_connection(self, conn: Connection) -> None:
key = self.get_thread_identifier()
if key in self.thread_connections:
raise dbt.exceptions.InternalException(
raise dbt.exceptions.DbtInternalError(
"In set_thread_connection, existing connection exists for {}"
)
self.thread_connections[key] = conn
@@ -136,7 +137,7 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
:return: A context manager that handles exceptions raised by the
underlying database.
"""
raise dbt.exceptions.NotImplementedException(
raise dbt.exceptions.NotImplementedError(
"`exception_handler` is not implemented for this adapter!"
)
@@ -169,7 +170,9 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
if conn.name == conn_name and conn.state == "open":
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":
fire_event(ConnectionReused(conn_name=conn_name))
@@ -208,7 +211,7 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
connect should trigger a retry.
:type retryable_exceptions: Iterable[Type[Exception]]
:param int retry_limit: How many times to retry the call to connect. If this limit
is exceeded before a successful call, a FailedToConnectException will be raised.
is exceeded before a successful call, a FailedToConnectError will be raised.
Must be non-negative.
:param retry_timeout: Time to wait between attempts to connect. Can also take a
Callable that takes the number of attempts so far, beginning at 0, and returns an int
@@ -217,14 +220,14 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
:param int _attempts: Parameter used to keep track of the number of attempts in calling the
connect function across recursive calls. Passed as an argument to retry_timeout if it
is a Callable. This parameter should not be set by the initial caller.
:raises dbt.exceptions.FailedToConnectException: Upon exhausting all retry attempts without
:raises dbt.exceptions.FailedToConnectError: Upon exhausting all retry attempts without
successfully acquiring a handle.
:return: The given connection with its appropriate state and handle attributes set
depending on whether we successfully acquired a handle or not.
"""
timeout = retry_timeout(_attempts) if callable(retry_timeout) else retry_timeout
if timeout < 0:
raise dbt.exceptions.FailedToConnectException(
raise dbt.exceptions.FailedToConnectError(
"retry_timeout cannot be negative or return a negative time."
)
@@ -232,7 +235,7 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
# This guard is not perfect others may add to the recursion limit (e.g. built-ins).
connection.handle = None
connection.state = ConnectionState.FAIL
raise dbt.exceptions.FailedToConnectException("retry_limit cannot be negative")
raise dbt.exceptions.FailedToConnectError("retry_limit cannot be negative")
try:
connection.handle = connect()
@@ -243,7 +246,7 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
if retry_limit <= 0:
connection.handle = None
connection.state = ConnectionState.FAIL
raise dbt.exceptions.FailedToConnectException(str(e))
raise dbt.exceptions.FailedToConnectError(str(e))
logger.debug(
f"Got a retryable error when attempting to open a {cls.TYPE} connection.\n"
@@ -265,12 +268,12 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
except Exception as e:
connection.handle = None
connection.state = ConnectionState.FAIL
raise dbt.exceptions.FailedToConnectException(str(e))
raise dbt.exceptions.FailedToConnectError(str(e))
@abc.abstractmethod
def cancel_open(self) -> Optional[List[str]]:
"""Cancel all open connections on the adapter. (passable)"""
raise dbt.exceptions.NotImplementedException(
raise dbt.exceptions.NotImplementedError(
"`cancel_open` is not implemented for this adapter!"
)
@@ -285,7 +288,7 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
This should be thread-safe, or hold the lock if necessary. The given
connection should not be in either in_use or available.
"""
raise dbt.exceptions.NotImplementedException("`open` is not implemented for this adapter!")
raise dbt.exceptions.NotImplementedError("`open` is not implemented for this adapter!")
def release(self) -> None:
with self.lock:
@@ -317,16 +320,12 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
@abc.abstractmethod
def begin(self) -> None:
"""Begin a transaction. (passable)"""
raise dbt.exceptions.NotImplementedException(
"`begin` is not implemented for this adapter!"
)
raise dbt.exceptions.NotImplementedError("`begin` is not implemented for this adapter!")
@abc.abstractmethod
def commit(self) -> None:
"""Commit a transaction. (passable)"""
raise dbt.exceptions.NotImplementedException(
"`commit` is not implemented for this adapter!"
)
raise dbt.exceptions.NotImplementedError("`commit` is not implemented for this adapter!")
@classmethod
def _rollback_handle(cls, connection: Connection) -> None:
@@ -336,7 +335,9 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
except Exception:
fire_event(
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,21 +346,27 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
"""Perform the actual close operation."""
# On windows, sometimes connection handles don't have a close() attr.
if hasattr(connection.handle, "close"):
fire_event(ConnectionClosed(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()
else:
fire_event(ConnectionLeftOpen(conn_name=cast_to_str(connection.name)))
fire_event(
ConnectionLeftOpen(
conn_name=cast_to_str(connection.name), node_info=get_node_info()
)
)
@classmethod
def _rollback(cls, connection: Connection) -> None:
"""Roll back the given connection."""
if connection.transaction_open is False:
raise dbt.exceptions.InternalException(
raise dbt.exceptions.DbtInternalError(
f"Tried to rollback transaction on connection "
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)
connection.transaction_open = False
@@ -371,7 +378,7 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
return connection
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)
connection.transaction_open = False
@@ -404,6 +411,4 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
:return: A tuple of the query status and results (empty if fetch=False).
:rtype: Tuple[AdapterResponse, agate.Table]
"""
raise dbt.exceptions.NotImplementedException(
"`execute` is not implemented for this adapter!"
)
raise dbt.exceptions.NotImplementedError("`execute` is not implemented for this adapter!")

View File

@@ -15,7 +15,6 @@ from typing import (
List,
Mapping,
Iterator,
Union,
Set,
)
@@ -23,13 +22,20 @@ import agate
import pytz
from dbt.exceptions import (
raise_database_error,
raise_compiler_error,
invalid_type_error,
get_relation_returned_multiple_results,
InternalException,
NotImplementedException,
RuntimeException,
DbtInternalError,
MacroArgTypeError,
MacroResultError,
QuoteConfigTypeError,
NotImplementedError,
NullRelationCacheAttemptedError,
NullRelationDropAttemptedError,
RelationReturnedMultipleResultsError,
RenameToNoneAttemptedError,
DbtRuntimeError,
SnapshotTargetIncompleteError,
SnapshotTargetNotSnapshotTableError,
UnexpectedNullError,
UnexpectedNonTimestampError,
)
from dbt.adapters.protocol import (
@@ -38,9 +44,8 @@ from dbt.adapters.protocol import (
)
from dbt.clients.agate_helper import empty_table, merge_tables, table_from_rows
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.parsed import ParsedSeedNode
from dbt.contracts.graph.nodes import ResultNode
from dbt.events.functions import fire_event, warn_or_error
from dbt.events.types import (
CacheMiss,
@@ -64,16 +69,13 @@ from dbt.adapters.base import Credentials
from dbt.adapters.cache import RelationsCache, _make_ref_key_msg
SeedModel = Union[ParsedSeedNode, CompiledSeedNode]
GET_CATALOG_MACRO_NAME = "get_catalog"
FRESHNESS_MACRO_NAME = "collect_freshness"
def _expect_row_value(key: str, row: agate.Row):
if key not in row.keys():
raise InternalException(
raise DbtInternalError(
'Got a row without "{}" column, columns: {}'.format(key, row.keys())
)
return row[key]
@@ -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.
"""
if dt is None:
raise raise_database_error(
"Expected a non-null value when querying field '{}' of table "
" {} but received value 'null' instead".format(field_name, source)
)
raise UnexpectedNullError(field_name, source)
elif not hasattr(dt, "tzinfo"):
raise raise_database_error(
"Expected a timestamp value when querying field '{}' of table "
"{} but received value of type '{}' instead".format(
field_name, source, type(dt).__name__
)
)
raise UnexpectedNonTimestampError(field_name, source, dt)
elif dt.tzinfo:
return dt.astimezone(pytz.UTC)
@@ -243,9 +237,7 @@ class BaseAdapter(metaclass=AdapterMeta):
return conn.name
@contextmanager
def connection_named(
self, name: str, node: Optional[CompileResultNode] = None
) -> Iterator[None]:
def connection_named(self, name: str, node: Optional[ResultNode] = None) -> Iterator[None]:
try:
if self.connections.query_header is not None:
self.connections.query_header.set(name, node)
@@ -257,7 +249,7 @@ class BaseAdapter(metaclass=AdapterMeta):
self.connections.query_header.reset()
@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):
yield
@@ -372,7 +364,7 @@ class BaseAdapter(metaclass=AdapterMeta):
lowercase strings.
"""
info_schema_name_map = SchemaSearchMap()
nodes: Iterator[CompileResultNode] = chain(
nodes: Iterator[ResultNode] = chain(
[
node
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`."""
if relation is None:
name = self.nice_connection_name()
raise_compiler_error("Attempted to cache a null relation for {}".format(name))
raise NullRelationCacheAttemptedError(name)
self.cache.add(relation)
# so jinja doesn't render things
return ""
@@ -453,7 +445,7 @@ class BaseAdapter(metaclass=AdapterMeta):
"""
if relation is None:
name = self.nice_connection_name()
raise_compiler_error("Attempted to drop a null relation for {}".format(name))
raise NullRelationDropAttemptedError(name)
self.cache.drop(relation)
return ""
@@ -470,9 +462,7 @@ class BaseAdapter(metaclass=AdapterMeta):
name = self.nice_connection_name()
src_name = _relation_name(from_relation)
dst_name = _relation_name(to_relation)
raise_compiler_error(
"Attempted to rename {} to {} for {}".format(src_name, dst_name, name)
)
raise RenameToNoneAttemptedError(src_name, dst_name, name)
self.cache.rename(from_relation, to_relation)
return ""
@@ -484,12 +474,12 @@ class BaseAdapter(metaclass=AdapterMeta):
@abc.abstractmethod
def date_function(cls) -> str:
"""Get the date function used by this adapter's database."""
raise NotImplementedException("`date_function` is not implemented for this adapter!")
raise NotImplementedError("`date_function` is not implemented for this adapter!")
@classmethod
@abc.abstractmethod
def is_cancelable(cls) -> bool:
raise NotImplementedException("`is_cancelable` is not implemented for this adapter!")
raise NotImplementedError("`is_cancelable` is not implemented for this adapter!")
###
# Abstract methods about schemas
@@ -497,7 +487,7 @@ class BaseAdapter(metaclass=AdapterMeta):
@abc.abstractmethod
def list_schemas(self, database: str) -> List[str]:
"""Get a list of existing schemas in database"""
raise NotImplementedException("`list_schemas` is not implemented for this adapter!")
raise NotImplementedError("`list_schemas` is not implemented for this adapter!")
@available.parse(lambda *a, **k: False)
def check_schema_exists(self, database: str, schema: str) -> bool:
@@ -520,13 +510,13 @@ class BaseAdapter(metaclass=AdapterMeta):
*Implementors must call self.cache.drop() to preserve cache state!*
"""
raise NotImplementedException("`drop_relation` is not implemented for this adapter!")
raise NotImplementedError("`drop_relation` is not implemented for this adapter!")
@abc.abstractmethod
@available.parse_none
def truncate_relation(self, relation: BaseRelation) -> None:
"""Truncate the given relation."""
raise NotImplementedException("`truncate_relation` is not implemented for this adapter!")
raise NotImplementedError("`truncate_relation` is not implemented for this adapter!")
@abc.abstractmethod
@available.parse_none
@@ -535,15 +525,13 @@ class BaseAdapter(metaclass=AdapterMeta):
Implementors must call self.cache.rename() to preserve cache state.
"""
raise NotImplementedException("`rename_relation` is not implemented for this adapter!")
raise NotImplementedError("`rename_relation` is not implemented for this adapter!")
@abc.abstractmethod
@available.parse_list
def get_columns_in_relation(self, relation: BaseRelation) -> List[BaseColumn]:
"""Get a list of the columns in the given Relation."""
raise NotImplementedException(
"`get_columns_in_relation` is not implemented for this adapter!"
)
raise NotImplementedError("`get_columns_in_relation` is not implemented for this adapter!")
@available.deprecated("get_columns_in_relation", lambda *a, **k: [])
def get_columns_in_table(self, schema: str, identifier: str) -> List[BaseColumn]:
@@ -565,7 +553,7 @@ class BaseAdapter(metaclass=AdapterMeta):
:param self.Relation current: A relation that currently exists in the
database with columns of unspecified types.
"""
raise NotImplementedException(
raise NotImplementedError(
"`expand_target_column_types` is not implemented for this adapter!"
)
@@ -580,7 +568,7 @@ class BaseAdapter(metaclass=AdapterMeta):
:return: The relations in schema
:rtype: List[self.Relation]
"""
raise NotImplementedException(
raise NotImplementedError(
"`list_relations_without_caching` is not implemented for this adapter!"
)
@@ -622,7 +610,7 @@ class BaseAdapter(metaclass=AdapterMeta):
to_relation.
"""
if not isinstance(from_relation, self.Relation):
invalid_type_error(
raise MacroArgTypeError(
method_name="get_missing_columns",
arg_name="from_relation",
got_value=from_relation,
@@ -630,7 +618,7 @@ class BaseAdapter(metaclass=AdapterMeta):
)
if not isinstance(to_relation, self.Relation):
invalid_type_error(
raise MacroArgTypeError(
method_name="get_missing_columns",
arg_name="to_relation",
got_value=to_relation,
@@ -651,11 +639,11 @@ class BaseAdapter(metaclass=AdapterMeta):
expected columns.
:param Relation relation: The relation to check
:raises CompilationException: If the columns are
:raises InvalidMacroArgType: If the columns are
incorrect.
"""
if not isinstance(relation, self.Relation):
invalid_type_error(
raise MacroArgTypeError(
method_name="valid_snapshot_target",
arg_name="relation",
got_value=relation,
@@ -676,24 +664,16 @@ class BaseAdapter(metaclass=AdapterMeta):
if missing:
if extra:
msg = (
'Snapshot target has ("{}") but not ("{}") - is it an '
"unmigrated previous version archive?".format(
'", "'.join(extra), '", "'.join(missing)
)
)
raise SnapshotTargetIncompleteError(extra, missing)
else:
msg = 'Snapshot target is not a snapshot table (missing "{}")'.format(
'", "'.join(missing)
)
raise_compiler_error(msg)
raise SnapshotTargetNotSnapshotTableError(missing)
@available.parse_none
def expand_target_column_types(
self, from_relation: BaseRelation, to_relation: BaseRelation
) -> None:
if not isinstance(from_relation, self.Relation):
invalid_type_error(
raise MacroArgTypeError(
method_name="expand_target_column_types",
arg_name="from_relation",
got_value=from_relation,
@@ -701,7 +681,7 @@ class BaseAdapter(metaclass=AdapterMeta):
)
if not isinstance(to_relation, self.Relation):
invalid_type_error(
raise MacroArgTypeError(
method_name="expand_target_column_types",
arg_name="to_relation",
got_value=to_relation,
@@ -783,7 +763,7 @@ class BaseAdapter(metaclass=AdapterMeta):
"schema": schema,
"database": database,
}
get_relation_returned_multiple_results(kwargs, matches)
raise RelationReturnedMultipleResultsError(kwargs, matches)
elif matches:
return matches[0]
@@ -805,20 +785,20 @@ class BaseAdapter(metaclass=AdapterMeta):
@available.parse_none
def create_schema(self, relation: BaseRelation):
"""Create the given schema if it does not exist."""
raise NotImplementedException("`create_schema` is not implemented for this adapter!")
raise NotImplementedError("`create_schema` is not implemented for this adapter!")
@abc.abstractmethod
@available.parse_none
def drop_schema(self, relation: BaseRelation):
"""Drop the given schema (and everything in it) if it exists."""
raise NotImplementedException("`drop_schema` is not implemented for this adapter!")
raise NotImplementedError("`drop_schema` is not implemented for this adapter!")
@available
@classmethod
@abc.abstractmethod
def quote(cls, identifier: str) -> str:
"""Quote the given identifier, as appropriate for the database."""
raise NotImplementedException("`quote` is not implemented for this adapter!")
raise NotImplementedError("`quote` is not implemented for this adapter!")
@available
def quote_as_configured(self, identifier: str, quote_key: str) -> str:
@@ -847,10 +827,7 @@ class BaseAdapter(metaclass=AdapterMeta):
elif quote_config is None:
pass
else:
raise_compiler_error(
f'The seed configuration value of "quote_columns" has an '
f"invalid type {type(quote_config)}"
)
raise QuoteConfigTypeError(quote_config)
if quote_columns:
return self.quote(column)
@@ -871,7 +848,7 @@ class BaseAdapter(metaclass=AdapterMeta):
:param col_idx: The index into the agate table for the column.
:return: The name of the type in the database
"""
raise NotImplementedException("`convert_text_type` is not implemented for this adapter!")
raise NotImplementedError("`convert_text_type` is not implemented for this adapter!")
@classmethod
@abc.abstractmethod
@@ -883,7 +860,7 @@ class BaseAdapter(metaclass=AdapterMeta):
:param col_idx: The index into the agate table for the column.
:return: The name of the type in the database
"""
raise NotImplementedException("`convert_number_type` is not implemented for this adapter!")
raise NotImplementedError("`convert_number_type` is not implemented for this adapter!")
@classmethod
@abc.abstractmethod
@@ -895,9 +872,7 @@ class BaseAdapter(metaclass=AdapterMeta):
:param col_idx: The index into the agate table for the column.
:return: The name of the type in the database
"""
raise NotImplementedException(
"`convert_boolean_type` is not implemented for this adapter!"
)
raise NotImplementedError("`convert_boolean_type` is not implemented for this adapter!")
@classmethod
@abc.abstractmethod
@@ -909,9 +884,7 @@ class BaseAdapter(metaclass=AdapterMeta):
:param col_idx: The index into the agate table for the column.
:return: The name of the type in the database
"""
raise NotImplementedException(
"`convert_datetime_type` is not implemented for this adapter!"
)
raise NotImplementedError("`convert_datetime_type` is not implemented for this adapter!")
@classmethod
@abc.abstractmethod
@@ -923,7 +896,7 @@ class BaseAdapter(metaclass=AdapterMeta):
:param col_idx: The index into the agate table for the column.
:return: The name of the type in the database
"""
raise NotImplementedException("`convert_date_type` is not implemented for this adapter!")
raise NotImplementedError("`convert_date_type` is not implemented for this adapter!")
@classmethod
@abc.abstractmethod
@@ -935,7 +908,7 @@ class BaseAdapter(metaclass=AdapterMeta):
:param col_idx: The index into the agate table for the column.
:return: The name of the type in the database
"""
raise NotImplementedException("`convert_time_type` is not implemented for this adapter!")
raise NotImplementedError("`convert_time_type` is not implemented for this adapter!")
@available
@classmethod
@@ -1002,7 +975,7 @@ class BaseAdapter(metaclass=AdapterMeta):
else:
package_name = 'the "{}" package'.format(project)
raise RuntimeException(
raise DbtRuntimeError(
'dbt could not find a macro with the name "{}" in {}'.format(
macro_name, package_name
)
@@ -1100,11 +1073,7 @@ class BaseAdapter(metaclass=AdapterMeta):
# now we have a 1-row table of the maximum `loaded_at_field` value and
# the current time according to the db.
if len(table) != 1 or len(table[0]) != 2:
raise_compiler_error(
'Got an invalid result from "{}" macro: {}'.format(
FRESHNESS_MACRO_NAME, [tuple(r) for r in table]
)
)
raise MacroResultError(FRESHNESS_MACRO_NAME, table)
if table[0][0] is None:
# no records in the table, so really the max_loaded_at was
# infinitely long ago. Just call it 0:00 January 1 year UTC
@@ -1181,7 +1150,7 @@ class BaseAdapter(metaclass=AdapterMeta):
elif location == "prepend":
return f"'{value}' || {add_to}"
else:
raise RuntimeException(f'Got an unexpected location value of "{location}"')
raise DbtRuntimeError(f'Got an unexpected location value of "{location}"')
def get_rows_different_sql(
self,
@@ -1239,7 +1208,7 @@ class BaseAdapter(metaclass=AdapterMeta):
return self.generate_python_submission_response(submission_result)
def generate_python_submission_response(self, submission_result: Any) -> AdapterResponse:
raise NotImplementedException(
raise NotImplementedError(
"Your adapter need to implement generate_python_submission_response"
)
@@ -1263,7 +1232,7 @@ class BaseAdapter(metaclass=AdapterMeta):
valid_strategies.append("default")
builtin_strategies = self.builtin_incremental_strategies()
if strategy in builtin_strategies and strategy not in valid_strategies:
raise RuntimeException(
raise DbtRuntimeError(
f"The incremental strategy '{strategy}' is not valid for this adapter"
)
@@ -1271,7 +1240,7 @@ class BaseAdapter(metaclass=AdapterMeta):
macro_name = f"get_incremental_{strategy}_sql"
# The model_context should have MacroGenerator callable objects for all macros
if macro_name not in model_context:
raise RuntimeException(
raise DbtRuntimeError(
'dbt could not find an incremental strategy macro with the name "{}" in {}'.format(
macro_name, self.config.project_name
)

View File

@@ -1,7 +1,7 @@
from typing import List, Optional, Type
from dbt.adapters.base import Credentials
from dbt.exceptions import CompilationException
from dbt.exceptions import CompilationError
from dbt.adapters.protocol import AdapterProtocol
@@ -11,7 +11,7 @@ def project_name_from_path(include_path: str) -> str:
partial = Project.partial_load(include_path)
if partial.project_name is None:
raise CompilationException(f"Invalid project at {include_path}: name not set!")
raise CompilationError(f"Invalid project at {include_path}: name not set!")
return partial.project_name

View File

@@ -5,9 +5,9 @@ from dbt.clients.jinja import QueryStringGenerator
from dbt.context.manifest import generate_query_header_context
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.exceptions import RuntimeException
from dbt.exceptions import DbtRuntimeError
class NodeWrapper:
@@ -48,7 +48,7 @@ class _QueryComment(local):
if isinstance(comment, str) and "*/" in comment:
# tell the user "no" so they don't hurt themselves by writing
# garbage
raise RuntimeException(f'query comment contains illegal value "*/": {comment}')
raise DbtRuntimeError(f'query comment contains illegal value "*/": {comment}')
self.query_comment = comment
self.append = append
@@ -90,7 +90,7 @@ class MacroQueryStringSetter:
def reset(self):
self.set("master", None)
def set(self, name: str, node: Optional[CompileResultNode]):
def set(self, name: str, node: Optional[ResultNode]):
wrapped: Optional[NodeWrapper] = None
if node is not None:
wrapped = NodeWrapper(node)

View File

@@ -1,9 +1,8 @@
from collections.abc import Hashable
from dataclasses import dataclass
from typing import Optional, TypeVar, Any, Type, Dict, Union, Iterator, Tuple, Set
from dataclasses import dataclass, field
from typing import Optional, TypeVar, Any, Type, Dict, Iterator, Tuple, Set
from dbt.contracts.graph.compiled import CompiledNode
from dbt.contracts.graph.parsed import ParsedSourceDefinition, ParsedNode
from dbt.contracts.graph.nodes import SourceDefinition, ManifestNode, ResultNode, ParsedNode
from dbt.contracts.relation import (
RelationType,
ComponentName,
@@ -12,7 +11,11 @@ from dbt.contracts.relation import (
Policy,
Path,
)
from dbt.exceptions import InternalException
from dbt.exceptions import (
ApproximateMatchError,
DbtInternalError,
MultipleDatabasesNotAllowedError,
)
from dbt.node_types import NodeType
from dbt.utils import filter_null_values, deep_merge, classproperty
@@ -27,8 +30,10 @@ class BaseRelation(FakeAPIObject, Hashable):
path: Path
type: Optional[RelationType] = None
quote_character: str = '"'
include_policy: Policy = Policy()
quote_policy: Policy = Policy()
# Python 3.11 requires that these use default_factory instead of simple default
# 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
def _is_exactish_match(self, field: ComponentName, value: str) -> bool:
@@ -39,9 +44,9 @@ class BaseRelation(FakeAPIObject, Hashable):
@classmethod
def _get_field_named(cls, field_name):
for field, _ in cls._get_fields():
if field.name == field_name:
return field
for f, _ in cls._get_fields():
if f.name == field_name:
return f
# this should be unreachable
raise ValueError(f"BaseRelation has no {field_name} field!")
@@ -52,11 +57,11 @@ class BaseRelation(FakeAPIObject, Hashable):
@classmethod
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
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):
"""Override `.get` to return a metadata object so we don't break
@@ -82,7 +87,7 @@ class BaseRelation(FakeAPIObject, Hashable):
if not search:
# nothing was passed in
raise dbt.exceptions.RuntimeException(
raise dbt.exceptions.DbtRuntimeError(
"Tried to match relation, but no search path was passed!"
)
@@ -99,7 +104,7 @@ class BaseRelation(FakeAPIObject, Hashable):
if approximate_match and not exact_match:
target = self.create(database=database, schema=schema, identifier=identifier)
dbt.exceptions.approximate_relation_match(target, self)
raise ApproximateMatchError(target, self)
return exact_match
@@ -184,7 +189,7 @@ class BaseRelation(FakeAPIObject, Hashable):
)
@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.pop("column", None)
quote_policy = deep_merge(
@@ -209,7 +214,7 @@ class BaseRelation(FakeAPIObject, Hashable):
def create_ephemeral_from_node(
cls: Type[Self],
config: HasQuoting,
node: Union[ParsedNode, CompiledNode],
node: ManifestNode,
) -> Self:
# Note that ephemeral models are based on the name.
identifier = cls.add_ephemeral_prefix(node.name)
@@ -222,7 +227,7 @@ class BaseRelation(FakeAPIObject, Hashable):
def create_from_node(
cls: Type[Self],
config: HasQuoting,
node: Union[ParsedNode, CompiledNode],
node: ManifestNode,
quote_policy: Optional[Dict[str, bool]] = None,
**kwargs: Any,
) -> Self:
@@ -243,20 +248,20 @@ class BaseRelation(FakeAPIObject, Hashable):
def create_from(
cls: Type[Self],
config: HasQuoting,
node: Union[CompiledNode, ParsedNode, ParsedSourceDefinition],
node: ResultNode,
**kwargs: Any,
) -> Self:
if node.resource_type == NodeType.Source:
if not isinstance(node, ParsedSourceDefinition):
raise InternalException(
"type mismatch, expected ParsedSourceDefinition but got {}".format(type(node))
if not isinstance(node, SourceDefinition):
raise DbtInternalError(
"type mismatch, expected SourceDefinition but got {}".format(type(node))
)
return cls.create_from_source(node, **kwargs)
else:
if not isinstance(node, (ParsedNode, CompiledNode)):
raise InternalException(
"type mismatch, expected ParsedNode or CompiledNode but "
"got {}".format(type(node))
# Can't use ManifestNode here because of parameterized generics
if not isinstance(node, (ParsedNode)):
raise DbtInternalError(
f"type mismatch, expected ManifestNode but got {type(node)}"
)
return cls.create_from_node(config, node, **kwargs)
@@ -353,7 +358,7 @@ class InformationSchema(BaseRelation):
def __post_init__(self):
if not isinstance(self.information_schema_view, (type(None), str)):
raise dbt.exceptions.CompilationException(
raise dbt.exceptions.CompilationError(
"Got an invalid name: {}".format(self.information_schema_view)
)
@@ -437,7 +442,7 @@ class SchemaSearchMap(Dict[InformationSchema, Set[Optional[str]]]):
if not allow_multiple_databases:
seen = {r.database.lower() for r in self if r.database}
if len(seen) > 1:
dbt.exceptions.raise_compiler_error(str(seen))
raise MultipleDatabasesNotAllowedError(seen)
for information_schema_name, schema in self.search():
path = {"database": information_schema_name.database, "schema": schema}

View File

@@ -1,4 +1,3 @@
import re
import threading
from copy import deepcopy
from typing import Any, Dict, Iterable, List, Optional, Set, Tuple
@@ -9,23 +8,15 @@ from dbt.adapters.reference_keys import (
_make_msg_from_ref_key,
_ReferenceKey,
)
import dbt.exceptions
from dbt.events.functions import fire_event, fire_event_if
from dbt.events.types import (
AddLink,
AddRelation,
DropCascade,
DropMissingRelation,
DropRelation,
DumpAfterAddGraph,
DumpAfterRenameSchema,
DumpBeforeAddGraph,
DumpBeforeRenameSchema,
RenameSchema,
TemporaryRelation,
UncachedRelation,
UpdateReference,
from dbt.exceptions import (
DependentLinkNotCachedError,
NewNameAlreadyInCacheError,
NoneRelationFoundError,
ReferencedLinkNotCachedError,
TruncatedModelNameCausedCollisionError,
)
from dbt.events.functions import fire_event, fire_event_if
from dbt.events.types import CacheAction, CacheDumpGraph
import dbt.flags as flags
from dbt.utils import lowercase
@@ -150,11 +141,7 @@ class _CachedRelation:
:raises InternalError: If the new key already exists.
"""
if new_key in self.referenced_by:
dbt.exceptions.raise_cache_inconsistent(
'in rename of "{}" -> "{}", new name is in the cache already'.format(
old_key, new_key
)
)
raise NewNameAlreadyInCacheError(old_key, new_key)
if old_key not in self.referenced_by:
return
@@ -270,21 +257,17 @@ class RelationsCache:
if referenced is None:
return
if referenced is None:
dbt.exceptions.raise_cache_inconsistent(
"in add_link, referenced link key {} not in cache!".format(referenced_key)
)
raise ReferencedLinkNotCachedError(referenced_key)
dependent = self.relations.get(dependent_key)
if dependent is None:
dbt.exceptions.raise_cache_inconsistent(
"in add_link, dependent link key {} not in cache!".format(dependent_key)
)
raise DependentLinkNotCachedError(dependent_key)
assert dependent is not None # we just raised!
referenced.add_reference(dependent)
# TODO: Is this dead code? I can't seem to find it grepping the codebase.
# This is called in plugins/postgres/dbt/adapters/postgres/impl.py
def add_link(self, referenced, dependent):
"""Add a link between two relations to the database. If either relation
does not exist, it will be added as an "external" relation.
@@ -306,9 +289,9 @@ class RelationsCache:
# referring to a table outside our control. There's no need to make
# a link - we will never drop the referenced relation during a run.
fire_event(
UncachedRelation(
dep_key=_make_msg_from_ref_key(dep_key),
CacheAction(
ref_key=_make_msg_from_ref_key(ref_key),
ref_key_2=_make_msg_from_ref_key(dep_key),
)
)
return
@@ -321,8 +304,10 @@ class RelationsCache:
dependent = dependent.replace(type=referenced.External)
self.add(dependent)
fire_event(
AddLink(
dep_key=_make_msg_from_ref_key(dep_key), ref_key=_make_msg_from_ref_key(ref_key)
CacheAction(
action="add_link",
ref_key=_make_msg_from_ref_key(dep_key),
ref_key_2=_make_msg_from_ref_key(ref_key),
)
)
with self.lock:
@@ -335,12 +320,18 @@ class RelationsCache:
:param BaseRelation relation: The underlying relation.
"""
cached = _CachedRelation(relation)
fire_event(AddRelation(relation=_make_ref_key_msg(cached)))
fire_event_if(flags.LOG_CACHE_EVENTS, lambda: DumpBeforeAddGraph(dump=self.dump_graph()))
fire_event_if(
flags.LOG_CACHE_EVENTS,
lambda: CacheDumpGraph(before_after="before", action="adding", dump=self.dump_graph()),
)
fire_event(CacheAction(action="add_relation", ref_key=_make_ref_key_msg(cached)))
with self.lock:
self._setdefault(cached)
fire_event_if(flags.LOG_CACHE_EVENTS, lambda: DumpAfterAddGraph(dump=self.dump_graph()))
fire_event_if(
flags.LOG_CACHE_EVENTS,
lambda: CacheDumpGraph(before_after="after", action="adding", dump=self.dump_graph()),
)
def _remove_refs(self, keys):
"""Removes all references to all entries in keys. This does not
@@ -368,16 +359,19 @@ class RelationsCache:
"""
dropped_key = _make_ref_key(relation)
dropped_key_msg = _make_ref_key_msg(relation)
fire_event(DropRelation(dropped=dropped_key_msg))
fire_event(CacheAction(action="drop_relation", ref_key=dropped_key_msg))
with self.lock:
if dropped_key not in self.relations:
fire_event(DropMissingRelation(relation=dropped_key_msg))
fire_event(CacheAction(action="drop_missing_relation", ref_key=dropped_key_msg))
return
consequences = self.relations[dropped_key].collect_consequences()
# convert from a list of _ReferenceKeys to a list of ReferenceKeyMsgs
consequence_msgs = [_make_msg_from_ref_key(key) for key in consequences]
fire_event(DropCascade(dropped=dropped_key_msg, consequences=consequence_msgs))
fire_event(
CacheAction(
action="drop_cascade", ref_key=dropped_key_msg, ref_list=consequence_msgs
)
)
self._remove_refs(consequences)
def _rename_relation(self, old_key, new_relation):
@@ -400,12 +394,14 @@ class RelationsCache:
for cached in self.relations.values():
if cached.is_referenced_by(old_key):
fire_event(
UpdateReference(
old_key=_make_ref_key_msg(old_key),
new_key=_make_ref_key_msg(new_key),
cached_key=_make_ref_key_msg(cached.key()),
CacheAction(
action="update_reference",
ref_key=_make_ref_key_msg(old_key),
ref_key_2=_make_ref_key_msg(new_key),
ref_key_3=_make_ref_key_msg(cached.key()),
)
)
cached.rename_key(old_key, new_key)
self.relations[new_key] = relation
@@ -430,27 +426,12 @@ class RelationsCache:
if new_key in self.relations:
# Tell user when collision caused by model names truncated during
# materialization.
match = re.search("__dbt_backup|__dbt_tmp$", new_key.identifier)
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
)
)
raise TruncatedModelNameCausedCollisionError(new_key, self.relations)
if old_key not in self.relations:
fire_event(TemporaryRelation(key=_make_msg_from_ref_key(old_key)))
fire_event(
CacheAction(action="temporary_relation", ref_key=_make_msg_from_ref_key(old_key))
)
return False
return True
@@ -469,13 +450,16 @@ class RelationsCache:
old_key = _make_ref_key(old)
new_key = _make_ref_key(new)
fire_event(
RenameSchema(
old_key=_make_msg_from_ref_key(old_key), new_key=_make_msg_from_ref_key(new)
CacheAction(
action="rename_relation",
ref_key=_make_msg_from_ref_key(old_key),
ref_key_2=_make_msg_from_ref_key(new),
)
)
fire_event_if(
flags.LOG_CACHE_EVENTS, lambda: DumpBeforeRenameSchema(dump=self.dump_graph())
flags.LOG_CACHE_EVENTS,
lambda: CacheDumpGraph(before_after="before", action="rename", dump=self.dump_graph()),
)
with self.lock:
@@ -485,7 +469,8 @@ class RelationsCache:
self._setdefault(_CachedRelation(new))
fire_event_if(
flags.LOG_CACHE_EVENTS, lambda: DumpAfterRenameSchema(dump=self.dump_graph())
flags.LOG_CACHE_EVENTS,
lambda: CacheDumpGraph(before_after="after", action="rename", dump=self.dump_graph()),
)
def get_relations(self, database: Optional[str], schema: Optional[str]) -> List[Any]:
@@ -505,9 +490,7 @@ class RelationsCache:
]
if None in results:
dbt.exceptions.raise_cache_inconsistent(
"in get_relations, a None relation was found in the cache!"
)
raise NoneRelationFoundError()
return results
def clear(self):

View File

@@ -10,7 +10,7 @@ from dbt.adapters.protocol import AdapterConfig, AdapterProtocol, RelationProtoc
from dbt.contracts.connection import AdapterRequiredConfig, Credentials
from dbt.events.functions import fire_event
from dbt.events.types import AdapterImportError, PluginLoadError
from dbt.exceptions import InternalException, RuntimeException
from dbt.exceptions import DbtInternalError, DbtRuntimeError
from dbt.include.global_project import PACKAGE_PATH as GLOBAL_PROJECT_PATH
from dbt.include.global_project import PROJECT_NAME as GLOBAL_PROJECT_NAME
@@ -34,7 +34,7 @@ class AdapterContainer:
names = ", ".join(self.plugins.keys())
message = f"Invalid adapter type {name}! Must be one of {names}"
raise RuntimeException(message)
raise DbtRuntimeError(message)
def get_adapter_class_by_name(self, name: str) -> Type[Adapter]:
plugin = self.get_plugin_by_name(name)
@@ -60,7 +60,7 @@ class AdapterContainer:
# the user about it via a runtime error
if exc.name == "dbt.adapters." + name:
fire_event(AdapterImportError(exc=str(exc)))
raise RuntimeException(f"Could not find adapter type {name}!")
raise DbtRuntimeError(f"Could not find adapter type {name}!")
# otherwise, the error had to have come from some underlying
# library. Log the stack trace.
@@ -70,7 +70,7 @@ class AdapterContainer:
plugin_type = plugin.adapter.type()
if plugin_type != name:
raise RuntimeException(
raise DbtRuntimeError(
f"Expected to find adapter with type named {name}, got "
f"adapter with type {plugin_type}"
)
@@ -132,7 +132,7 @@ class AdapterContainer:
try:
plugin = self.plugins[plugin_name]
except KeyError:
raise InternalException(f"No plugin found for {plugin_name}") from None
raise DbtInternalError(f"No plugin found for {plugin_name}") from None
plugins.append(plugin)
seen.add(plugin_name)
for dep in plugin.dependencies:
@@ -151,7 +151,7 @@ class AdapterContainer:
try:
path = self.packages[package_name]
except KeyError:
raise InternalException(f"No internal package listing found for {package_name}")
raise DbtInternalError(f"No internal package listing found for {package_name}")
paths.append(path)
return paths

View File

@@ -8,7 +8,6 @@ from typing import (
Generic,
TypeVar,
Tuple,
Union,
Dict,
Any,
)
@@ -17,8 +16,7 @@ from typing_extensions import Protocol
import agate
from dbt.contracts.connection import Connection, AdapterRequiredConfig, AdapterResponse
from dbt.contracts.graph.compiled import CompiledNode, ManifestNode, NonSourceCompiledNode
from dbt.contracts.graph.parsed import ParsedNode, ParsedSourceDefinition
from dbt.contracts.graph.nodes import ResultNode, ManifestNode
from dbt.contracts.graph.model_config import BaseConfig
from dbt.contracts.graph.manifest import Manifest
from dbt.contracts.relation import Policy, HasQuoting
@@ -48,11 +46,7 @@ class RelationProtocol(Protocol):
...
@classmethod
def create_from(
cls: Type[Self],
config: HasQuoting,
node: Union[CompiledNode, ParsedNode, ParsedSourceDefinition],
) -> Self:
def create_from(cls: Type[Self], config: HasQuoting, node: ResultNode) -> Self:
...
@@ -65,7 +59,7 @@ class CompilerProtocol(Protocol):
node: ManifestNode,
manifest: Manifest,
extra_context: Optional[Dict[str, Any]] = None,
) -> NonSourceCompiledNode:
) -> ManifestNode:
...

View File

@@ -10,6 +10,7 @@ from dbt.adapters.base import BaseConnectionManager
from dbt.contracts.connection import Connection, ConnectionState, AdapterResponse
from dbt.events.functions import fire_event
from dbt.events.types import ConnectionUsed, SQLQuery, SQLCommit, SQLQueryStatus
from dbt.events.contextvars import get_node_info
from dbt.utils import cast_to_str
@@ -26,9 +27,7 @@ class SQLConnectionManager(BaseConnectionManager):
@abc.abstractmethod
def cancel(self, connection: Connection):
"""Cancel the given connection."""
raise dbt.exceptions.NotImplementedException(
"`cancel` is not implemented for this adapter!"
)
raise dbt.exceptions.NotImplementedError("`cancel` is not implemented for this adapter!")
def cancel_open(self) -> List[str]:
names = []
@@ -56,7 +55,13 @@ class SQLConnectionManager(BaseConnectionManager):
connection = self.get_thread_connection()
if auto_begin and connection.transaction_open is False:
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):
if abridge_sql_log:
@@ -64,7 +69,11 @@ class SQLConnectionManager(BaseConnectionManager):
else:
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()
cursor = connection.handle.cursor()
@@ -72,7 +81,9 @@ class SQLConnectionManager(BaseConnectionManager):
fire_event(
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(),
)
)
@@ -82,7 +93,7 @@ class SQLConnectionManager(BaseConnectionManager):
@abc.abstractmethod
def get_response(cls, cursor: Any) -> AdapterResponse:
"""Get the status of the cursor."""
raise dbt.exceptions.NotImplementedException(
raise dbt.exceptions.NotImplementedError(
"`get_response` is not implemented for this adapter!"
)
@@ -138,7 +149,7 @@ class SQLConnectionManager(BaseConnectionManager):
def begin(self):
connection = self.get_thread_connection()
if connection.transaction_open is True:
raise dbt.exceptions.InternalException(
raise dbt.exceptions.DbtInternalError(
'Tried to begin a new transaction on connection "{}", but '
"it already had one open!".format(connection.name)
)
@@ -151,12 +162,12 @@ class SQLConnectionManager(BaseConnectionManager):
def commit(self):
connection = self.get_thread_connection()
if connection.transaction_open is False:
raise dbt.exceptions.InternalException(
raise dbt.exceptions.DbtInternalError(
'Tried to commit transaction on connection "{}", but '
"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()
connection.transaction_open = False

View File

@@ -1,9 +1,8 @@
import agate
from typing import Any, Optional, Tuple, Type, List
import dbt.clients.agate_helper
from dbt.contracts.connection import Connection
import dbt.exceptions
from dbt.exceptions import RelationTypeNullError
from dbt.adapters.base import BaseAdapter, available
from dbt.adapters.cache import _make_ref_key_msg
from dbt.adapters.sql import SQLConnectionManager
@@ -132,9 +131,7 @@ class SQLAdapter(BaseAdapter):
def drop_relation(self, relation):
if relation.type is None:
dbt.exceptions.raise_compiler_error(
"Tried to drop relation {}, but its type is null.".format(relation)
)
raise RelationTypeNullError(relation)
self.cache_dropped(relation)
self.execute_macro(DROP_RELATION_MACRO_NAME, kwargs={"relation": relation})

View File

@@ -31,7 +31,6 @@ def cli_runner():
@p.cache_selected_only
@p.debug
@p.enable_legacy_logger
@p.event_buffer_size
@p.fail_fast
@p.log_cache_events
@p.log_format
@@ -47,6 +46,7 @@ def cli_runner():
@p.version
@p.version_check
@p.warn_error
@p.warn_error_options
@p.write_json
def cli(ctx, **kwargs):
"""An ELT tool for managing your SQL transformations and data models.

View File

@@ -1,6 +1,8 @@
from click import ParamType
import yaml
from dbt.helper_types import WarnErrorOptions
class YAML(ParamType):
"""The Click YAML type. Converts YAML strings into objects."""
@@ -17,6 +19,19 @@ class YAML(ParamType):
self.fail(f"String '{value}' is not valid YAML", param, ctx)
class WarnErrorOptionsType(YAML):
"""The Click WarnErrorOptions type. Converts YAML strings into objects."""
name = "WarnErrorOptionsType"
def convert(self, value, param, ctx):
include_exclude = super().convert(value, param, ctx)
return WarnErrorOptions(
include=include_exclude.get("include", []), exclude=include_exclude.get("exclude", [])
)
class Truthy(ParamType):
"""The Click Truthy type. Converts strings into a "truthy" type"""

View File

@@ -1,7 +1,7 @@
from pathlib import Path, PurePath
import click
from dbt.cli.option_types import YAML
from dbt.cli.option_types import YAML, WarnErrorOptionsType
from dbt.cli.resolvers import default_project_dir, default_profiles_dir
@@ -80,14 +80,6 @@ enable_legacy_logger = click.option(
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.")
fail_fast = click.option(
@@ -278,7 +270,7 @@ show = click.option(
)
skip_profile_setup = click.option(
"--skip-profile-setup", "-s", envvar=None, help="Skip interative profile setup.", is_flag=True
"--skip-profile-setup", "-s", envvar=None, help="Skip interactive profile setup.", is_flag=True
)
# TODO: The env var and name (reflected in flags) are corrections!
@@ -366,9 +358,20 @@ version_check = click.option(
)
warn_error = click.option(
"--warn-error/--no-warn-error",
"--warn-error",
envvar="DBT_WARN_ERROR",
help="If dbt would normally warn, instead raise an exception. Examples include --models that selects nothing, deprecations, configurations with no associated models, invalid test configurations, and missing sources/refs in tests.",
help="If dbt would normally warn, instead raise an exception. Examples include --select that selects nothing, deprecations, configurations with no associated models, invalid test configurations, and missing sources/refs in tests.",
default=None,
flag_value=True,
)
warn_error_options = click.option(
"--warn-error-options",
envvar="DBT_WARN_ERROR_OPTIONS",
default=None,
help="""If dbt would normally warn, instead raise an exception based on include/exclude configuration. Examples include --select that selects nothing, deprecations, configurations with no associated models, invalid test configurations,
and missing sources/refs in tests. This argument should be a YAML string, with keys 'include' or 'exclude'. eg. '{"include": "all", "exclude": ["NoNodesForSelectionCriteria"]}'""",
type=WarnErrorOptionsType(),
)
write_json = click.option(

View File

@@ -1,7 +1,15 @@
import re
from collections import namedtuple
import dbt.exceptions
from dbt.exceptions import (
BlockDefinitionNotAtTopError,
DbtInternalError,
MissingCloseTagError,
MissingControlFlowStartTagError,
NestedTagsError,
UnexpectedControlFlowEndTagError,
UnexpectedMacroEOFError,
)
def regex(pat):
@@ -139,10 +147,7 @@ class TagIterator:
def _expect_match(self, expected_name, *patterns, **kwargs):
match = self._first_match(*patterns, **kwargs)
if match is None:
msg = 'unexpected EOF, expected {}, got "{}"'.format(
expected_name, self.data[self.pos :]
)
dbt.exceptions.raise_compiler_error(msg)
raise UnexpectedMacroEOFError(expected_name, self.data[self.pos :])
return match
def handle_expr(self, match):
@@ -256,7 +261,7 @@ class TagIterator:
elif block_type_name is not None:
yield self.handle_tag(match)
else:
raise dbt.exceptions.InternalException(
raise DbtInternalError(
"Invalid regex match in next_block, expected block start, "
"expr start, or comment start"
)
@@ -265,13 +270,6 @@ class TagIterator:
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 = {
"if": "endif",
"for": "endfor",
@@ -319,33 +317,16 @@ class BlockIterator:
found = self.stack.pop()
else:
expected = _CONTROL_FLOW_END_TAGS[tag.block_type_name]
dbt.exceptions.raise_compiler_error(
(
"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))
)
raise UnexpectedControlFlowEndTagError(tag, expected, self.tag_parser)
expected = _CONTROL_FLOW_TAGS[found]
if expected != tag.block_type_name:
dbt.exceptions.raise_compiler_error(
(
"Got an unexpected control flow end tag, got {} but "
"expected {} next (@ {})"
).format(tag.block_type_name, expected, self.tag_parser.linepos(tag.start))
)
raise MissingControlFlowStartTagError(tag, expected, self.tag_parser)
if tag.block_type_name in allowed_blocks:
if self.stack:
dbt.exceptions.raise_compiler_error(
(
"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))
)
raise BlockDefinitionNotAtTopError(self.tag_parser, tag.start)
if self.current is not None:
dbt.exceptions.raise_compiler_error(
duplicate_tags.format(outer=self.current, inner=tag)
)
raise NestedTagsError(outer=self.current, inner=tag)
if collect_raw_data:
raw_data = self.data[self.last_position : tag.start]
self.last_position = tag.start
@@ -366,11 +347,7 @@ class BlockIterator:
if self.current:
linecount = self.data[: self.current.end].count("\n") + 1
dbt.exceptions.raise_compiler_error(
("Reached EOF without finding a close tag for {} (searched from line {})").format(
self.current.block_type_name, linecount
)
)
raise MissingCloseTagError(self.current.block_type_name, linecount)
if collect_raw_data:
raw_data = self.data[self.last_position :]

View File

@@ -7,7 +7,7 @@ import json
import dbt.utils
from typing import Iterable, List, Dict, Union, Optional, Any
from dbt.exceptions import RuntimeException
from dbt.exceptions import DbtRuntimeError
BOM = BOM_UTF8.decode("utf-8") # '\ufeff'
@@ -168,7 +168,7 @@ class ColumnTypeBuilder(Dict[str, NullableAgateType]):
return
elif not isinstance(value, type(existing_type)):
# actual type mismatch!
raise RuntimeException(
raise DbtRuntimeError(
f"Tables contain columns with the same names ({key}), "
f"but different types ({value} vs {existing_type})"
)

View File

@@ -14,10 +14,10 @@ from dbt.events.types import (
)
from dbt.exceptions import (
CommandResultError,
RuntimeException,
bad_package_spec,
raise_git_cloning_error,
raise_git_cloning_problem,
GitCheckoutError,
GitCloningError,
UnknownGitCloningProblemError,
DbtRuntimeError,
)
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))
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):
has_revision = revision is not None
is_commit = _is_commit(revision or "")
@@ -64,7 +54,7 @@ def clone(repo, cwd, dirname=None, remove_git_dir=False, revision=None, subdirec
try:
result = run_cmd(cwd, clone_cmd, env={"LC_ALL": "C"})
except CommandResultError as exc:
_raise_git_cloning_error(repo, revision, exc)
raise GitCloningError(repo, revision, exc)
if subdirectory:
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:
run_cmd(cwd_subdir, clone_cmd_subdir)
except CommandResultError as exc:
_raise_git_cloning_error(repo, revision, exc)
raise GitCloningError(repo, revision, exc)
if remove_git_dir:
rmdir(os.path.join(dirname, ".git"))
@@ -115,8 +105,7 @@ def checkout(cwd, repo, revision=None):
try:
return _checkout(cwd, repo, revision)
except CommandResultError as exc:
stderr = exc.stderr.strip()
bad_package_spec(repo, revision, stderr)
raise GitCheckoutError(repo=repo, revision=revision, error=exc)
def get_current_sha(cwd):
@@ -145,7 +134,7 @@ def clone_and_checkout(
err = exc.stderr
exists = re.match("fatal: destination path '(.+)' already exists", err)
if not exists:
raise_git_cloning_problem(repo)
raise UnknownGitCloningProblemError(repo)
directory = None
start_sha = None
@@ -155,7 +144,7 @@ def clone_and_checkout(
else:
matches = re.match("Cloning into '(.+)'", err.decode("utf-8"))
if matches is None:
raise RuntimeException(f'Error cloning {repo} - never saw "Cloning into ..." from git')
raise DbtRuntimeError(f'Error cloning {repo} - never saw "Cloning into ..." from git')
directory = matches.group(1)
fire_event(GitProgressPullingNewDependency(dir=directory))
full_path = os.path.join(cwd, directory)

View File

@@ -25,17 +25,20 @@ from dbt.utils import (
)
from dbt.clients._jinja_blocks import BlockIterator, BlockData, BlockTag
from dbt.contracts.graph.compiled import CompiledGenericTestNode
from dbt.contracts.graph.parsed import ParsedGenericTestNode
from dbt.contracts.graph.nodes import GenericTestNode
from dbt.exceptions import (
InternalException,
raise_compiler_error,
CompilationException,
invalid_materialization_argument,
CaughtMacroError,
CaughtMacroErrorWithNodeError,
CompilationError,
DbtInternalError,
MaterializationArgError,
JinjaRenderingError,
MacroReturn,
JinjaRenderingException,
UndefinedMacroException,
MaterializtionMacroNotUsedError,
NoSupportedLanguagesFoundError,
UndefinedCompilationError,
UndefinedMacroError,
)
from dbt import flags
from dbt.node_types import ModelLanguage
@@ -158,9 +161,9 @@ def quoted_native_concat(nodes):
except (ValueError, SyntaxError, MemoryError):
result = raw
if isinstance(raw, BoolMarker) and not isinstance(result, bool):
raise JinjaRenderingException(f"Could not convert value '{raw!s}' into type 'bool'")
raise JinjaRenderingError(f"Could not convert value '{raw!s}' into type 'bool'")
if isinstance(raw, NumberMarker) and not _is_number(result):
raise JinjaRenderingException(f"Could not convert value '{raw!s}' into type 'number'")
raise JinjaRenderingError(f"Could not convert value '{raw!s}' into type 'number'")
return result
@@ -238,12 +241,12 @@ class BaseMacroGenerator:
try:
yield
except (TypeError, jinja2.exceptions.TemplateRuntimeError) as e:
raise_compiler_error(str(e))
raise CaughtMacroError(e)
def call_macro(self, *args, **kwargs):
# called from __call__ methods
if self.context is None:
raise InternalException("Context is still None in call_macro!")
raise DbtInternalError("Context is still None in call_macro!")
assert self.context is not None
macro = self.get_macro()
@@ -270,7 +273,7 @@ class MacroStack(threading.local):
def pop(self, name):
got = self.call_stack.pop()
if got != name:
raise InternalException(f"popped {got}, expected {name}")
raise DbtInternalError(f"popped {got}, expected {name}")
class MacroGenerator(BaseMacroGenerator):
@@ -297,8 +300,8 @@ class MacroGenerator(BaseMacroGenerator):
try:
yield
except (TypeError, jinja2.exceptions.TemplateRuntimeError) as e:
raise_compiler_error(str(e), self.macro)
except CompilationException as e:
raise CaughtMacroErrorWithNodeError(exc=e, node=self.macro)
except CompilationError as e:
e.stack.append(self.macro)
raise e
@@ -377,7 +380,7 @@ class MaterializationExtension(jinja2.ext.Extension):
node.defaults.append(languages)
else:
invalid_materialization_argument(materialization_name, target.name)
raise MaterializationArgError(materialization_name, target.name)
if SUPPORTED_LANG_ARG not in node.args:
node.args.append(SUPPORTED_LANG_ARG)
@@ -452,7 +455,7 @@ def create_undefined(node=None):
return self
def __reduce__(self):
raise_compiler_error(f"{self.name} is undefined", node=node)
raise UndefinedCompilationError(name=self.name, node=node)
return Undefined
@@ -510,10 +513,10 @@ def catch_jinja(node=None) -> Iterator[None]:
yield
except jinja2.exceptions.TemplateSyntaxError as e:
e.translated = False
raise CompilationException(str(e), node) from e
raise CompilationError(str(e), node) from e
except jinja2.exceptions.UndefinedError as e:
raise UndefinedMacroException(str(e), node) from e
except CompilationException as exc:
raise UndefinedMacroError(str(e), node) from e
except CompilationError as exc:
exc.add_node(node)
raise
@@ -620,7 +623,7 @@ GENERIC_TEST_KWARGS_NAME = "_dbt_generic_test_kwargs"
def add_rendered_test_kwargs(
context: Dict[str, Any],
node: Union[ParsedGenericTestNode, CompiledGenericTestNode],
node: GenericTestNode,
capture_macros: bool = False,
) -> None:
"""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]:
if "materialization" not in node.name:
raise_compiler_error("Only materialization macros can be used with this function")
raise MaterializtionMacroNotUsedError(node=node)
no_kwargs = not node.defaults
no_langs_found = SUPPORTED_LANG_ARG not in node.args
if no_kwargs or no_langs_found:
raise_compiler_error(f"No supported_languages found in materialization macro {node.name}")
raise NoSupportedLanguagesFoundError(node=node)
lang_idx = node.args.index(SUPPORTED_LANG_ARG)
# indexing defaults from the end

View File

@@ -1,6 +1,6 @@
import jinja2
from dbt.clients.jinja import get_environment
from dbt.exceptions import raise_compiler_error
from dbt.exceptions import MacroNamespaceNotStringError, MacroNameNotStringError
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
possible_macro_calls.append(func_name)
else:
raise_compiler_error(
f"The macro_name parameter ({kwarg.value.value}) "
"to adapter.dispatch was not a string"
)
raise MacroNameNotStringError(kwarg_value=kwarg.value.value)
elif kwarg.key == "macro_namespace":
# This will remain to enable static resolution
kwarg_type = type(kwarg.value).__name__
if kwarg_type == "Const":
macro_namespace = kwarg.value.value
else:
raise_compiler_error(
"The macro_namespace parameter to adapter.dispatch "
f"is a {kwarg_type}, not a string"
)
raise MacroNamespaceNotStringError(kwarg_type)
# positional arguments
if packages_arg:

View File

@@ -19,8 +19,8 @@ from dbt.events.types import (
SystemErrorRetrievingModTime,
SystemCouldNotWrite,
SystemExecutingCmd,
SystemStdOutMsg,
SystemStdErrMsg,
SystemStdOut,
SystemStdErr,
SystemReportReturnCode,
)
import dbt.exceptions
@@ -144,7 +144,8 @@ def make_symlink(source: str, link_path: str) -> None:
Create a symlink at `link_path` referring to `source`.
"""
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)
@@ -411,7 +412,7 @@ def _interpret_oserror(exc: OSError, cwd: str, cmd: List[str]) -> NoReturn:
_handle_posix_error(exc, cwd, cmd)
# this should not be reachable, raise _something_ at least!
raise dbt.exceptions.InternalException(
raise dbt.exceptions.DbtInternalError(
"Unhandled exception in _interpret_oserror: {}".format(exc)
)
@@ -440,8 +441,8 @@ def run_cmd(cwd: str, cmd: List[str], env: Optional[Dict[str, Any]] = None) -> T
except OSError as exc:
_interpret_oserror(exc, cwd, cmd)
fire_event(SystemStdOutMsg(bmsg=out))
fire_event(SystemStdErrMsg(bmsg=err))
fire_event(SystemStdOut(bmsg=out))
fire_event(SystemStdErr(bmsg=err))
if proc.returncode != 0:
fire_event(SystemReportReturnCode(returncode=proc.returncode))

View File

@@ -60,4 +60,4 @@ def load_yaml_text(contents, path=None):
else:
error = str(e)
raise dbt.exceptions.ValidationException(error)
raise dbt.exceptions.DbtValidationError(error)

View File

@@ -1,6 +1,6 @@
import os
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 pickle
@@ -12,23 +12,23 @@ from dbt.clients import jinja
from dbt.clients.system import make_directory
from dbt.context.providers import generate_runtime_model_context
from dbt.contracts.graph.manifest import Manifest, UniqueID
from dbt.contracts.graph.compiled import (
COMPILED_TYPES,
CompiledGenericTestNode,
from dbt.contracts.graph.nodes import (
ManifestNode,
ManifestSQLNode,
GenericTestNode,
GraphMemberNode,
InjectedCTE,
ManifestNode,
NonSourceCompiledNode,
SeedNode,
)
from dbt.contracts.graph.parsed import ParsedNode
from dbt.exceptions import (
dependency_not_found,
InternalException,
RuntimeException,
GraphDependencyNotFoundError,
DbtInternalError,
DbtRuntimeError,
)
from dbt.graph import Graph
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.events.format import pluralize
import dbt.tracking
@@ -36,14 +36,6 @@ import dbt.tracking
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):
names = {
NodeType.Model: "model",
@@ -176,7 +168,7 @@ class Compiler:
# a dict for jinja rendering of SQL
def _create_node_context(
self,
node: NonSourceCompiledNode,
node: ManifestSQLNode,
manifest: Manifest,
extra_context: Dict[str, Any],
) -> Dict[str, Any]:
@@ -184,7 +176,7 @@ class Compiler:
context = generate_runtime_model_context(node, self.config, manifest)
context.update(extra_context)
if isinstance(node, CompiledGenericTestNode):
if isinstance(node, GenericTestNode):
# for test nodes, add a special keyword args value to the context
jinja.add_rendered_test_kwargs(context, node)
@@ -195,14 +187,6 @@ class Compiler:
relation_cls = adapter.Relation
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:
"""
`ctes` is a list of InjectedCTEs like:
@@ -261,10 +245,10 @@ class Compiler:
def _recursively_prepend_ctes(
self,
model: NonSourceCompiledNode,
model: ManifestSQLNode,
manifest: Manifest,
extra_context: Optional[Dict[str, Any]],
) -> Tuple[NonSourceCompiledNode, List[InjectedCTE]]:
) -> Tuple[ManifestSQLNode, List[InjectedCTE]]:
"""This method is called by the 'compile_node' method. Starting
from the node that it is passed in, it will recursively call
itself using the 'extra_ctes'. The 'ephemeral' models do
@@ -273,13 +257,14 @@ class Compiler:
inserting CTEs into the SQL.
"""
if model.compiled_code is None:
raise RuntimeException("Cannot inject ctes into an unparsed node", model)
raise DbtRuntimeError("Cannot inject ctes into an unparsed node", model)
if model.extra_ctes_injected:
return (model, model.extra_ctes)
# Just to make it plain that nothing is actually injected for this case
if not model.extra_ctes:
model.extra_ctes_injected = True
if not isinstance(model, SeedNode):
model.extra_ctes_injected = True
manifest.update_node(model)
return (model, model.extra_ctes)
@@ -293,20 +278,19 @@ class Compiler:
# ephemeral model.
for cte in model.extra_ctes:
if cte.id not in manifest.nodes:
raise InternalException(
raise DbtInternalError(
f"During compilation, found a cte reference that "
f"could not be resolved: {cte.id}"
)
cte_model = manifest.nodes[cte.id]
assert not isinstance(cte_model, SeedNode)
if not cte_model.is_ephemeral_model:
raise InternalException(f"{cte.id} is not ephemeral")
raise DbtInternalError(f"{cte.id} is not ephemeral")
# This model has already been compiled, so it's been
# through here before
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
# if the cte_model isn't compiled, i.e. first time here
@@ -343,21 +327,19 @@ class Compiler:
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,
# and then renders the "compiled_code" using the node, the
# raw_code and the context.
def _compile_node(
self,
node: ManifestNode,
node: ManifestSQLNode,
manifest: Manifest,
extra_context: Optional[Dict[str, Any]] = None,
) -> NonSourceCompiledNode:
) -> ManifestSQLNode:
if extra_context is None:
extra_context = {}
fire_event(CompilingNode(unique_id=node.unique_id))
data = node.to_dict(omit_none=True)
data.update(
{
@@ -367,9 +349,8 @@ class Compiler:
"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
# 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
original_quoting = self.config.quoting
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(
"{{ py_script_postfix(model) }}",
@@ -385,23 +366,33 @@ class Compiler:
node,
)
# 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
self.config.quoting = original_quoting
else:
context = self._create_node_context(compiled_node, manifest, extra_context)
compiled_node.compiled_code = jinja.get_rendered(
context = self._create_node_context(node, manifest, extra_context)
node.compiled_code = jinja.get_rendered(
node.raw_code,
context,
node,
)
compiled_node.relation_name = self._get_relation_name(node)
# relation_name is set at parse time, except for tests without store_failures,
# but cli param can turn on store_failures, so we set here.
if (
node.resource_type == NodeType.Test
and node.relation_name is None
and node.is_relational
):
adapter = get_adapter(self.config)
relation_cls = adapter.Relation
relation_name = str(relation_cls.create_from(self.config, node))
node.relation_name = relation_name
compiled_node.compiled = True
node.compiled = True
return compiled_node
return node
def write_graph_file(self, linker: Linker, manifest: Manifest):
filename = graph_file_name
@@ -420,7 +411,7 @@ class Compiler:
elif dependency in manifest.metrics:
linker.dependency(node.unique_id, (manifest.metrics[dependency].unique_id))
else:
dependency_not_found(node, dependency)
raise GraphDependencyNotFoundError(node, dependency)
def link_graph(self, linker: Linker, manifest: Manifest, add_test_edges: bool = False):
for source in manifest.sources.values():
@@ -508,10 +499,13 @@ class Compiler:
return Graph(linker.graph)
# writes the "compiled_code" into the target/compiled directory
def _write_node(self, node: NonSourceCompiledNode) -> ManifestNode:
if not node.extra_ctes_injected or node.resource_type == NodeType.Snapshot:
def _write_node(self, node: ManifestSQLNode) -> ManifestSQLNode:
if not node.extra_ctes_injected or node.resource_type in (
NodeType.Snapshot,
NodeType.Seed,
):
return node
fire_event(WritingInjectedSQLForNode(unique_id=node.unique_id))
fire_event(WritingInjectedSQLForNode(node_info=get_node_info()))
if node.compiled_code:
node.compiled_path = node.write_node(
@@ -521,11 +515,11 @@ class Compiler:
def compile_node(
self,
node: ManifestNode,
node: ManifestSQLNode,
manifest: Manifest,
extra_context: Optional[Dict[str, Any]] = None,
write: bool = True,
) -> NonSourceCompiledNode:
) -> ManifestSQLNode:
"""This is the main entry point into this code. It's called by
CompileRunner.compile, GenericRPCRunner.compile, and
RunTask.get_hook_sql. It calls '_compile_node' to convert

View File

@@ -9,12 +9,14 @@ from dbt.clients.system import load_file_contents
from dbt.clients.yaml_helper import load_yaml_text
from dbt.contracts.connection import Credentials, HasCredentials
from dbt.contracts.project import ProfileConfig, UserConfig
from dbt.exceptions import CompilationException
from dbt.exceptions import DbtProfileError
from dbt.exceptions import DbtProjectError
from dbt.exceptions import ValidationException
from dbt.exceptions import RuntimeException
from dbt.exceptions import validator_error_message
from dbt.exceptions import (
CompilationError,
DbtProfileError,
DbtProjectError,
DbtValidationError,
DbtRuntimeError,
ProfileConfigError,
)
from dbt.events.types import MissingProfileTarget
from dbt.events.functions import fire_event
from dbt.utils import coerce_dict_str
@@ -58,9 +60,9 @@ def read_profile(profiles_dir: str) -> Dict[str, Any]:
msg = f"The profiles.yml file at {path} is empty"
raise DbtProfileError(INVALID_PROFILE_MESSAGE.format(error_string=msg))
return yaml_content
except ValidationException as e:
except DbtValidationError as e:
msg = INVALID_PROFILE_MESSAGE.format(error_string=e)
raise ValidationException(msg) from e
raise DbtValidationError(msg) from e
return {}
@@ -73,7 +75,7 @@ def read_user_config(directory: str) -> UserConfig:
if user_config is not None:
UserConfig.validate(user_config)
return UserConfig.from_dict(user_config)
except (RuntimeException, ValidationError):
except (DbtRuntimeError, ValidationError):
pass
return UserConfig()
@@ -156,7 +158,7 @@ class Profile(HasCredentials):
dct = self.to_profile_info(serialize_credentials=True)
ProfileConfig.validate(dct)
except ValidationError as exc:
raise DbtProfileError(validator_error_message(exc)) from exc
raise ProfileConfigError(exc) from exc
@staticmethod
def _credentials_from_profile(
@@ -180,8 +182,8 @@ class Profile(HasCredentials):
data = cls.translate_aliases(profile)
cls.validate(data)
credentials = cls.from_dict(data)
except (RuntimeException, ValidationError) as e:
msg = str(e) if isinstance(e, RuntimeException) else e.message
except (DbtRuntimeError, ValidationError) as e:
msg = str(e) if isinstance(e, DbtRuntimeError) else e.message
raise DbtProfileError(
'Credentials in profile "{}", target "{}" invalid: {}'.format(
profile_name, target_name, msg
@@ -297,7 +299,7 @@ class Profile(HasCredentials):
try:
profile_data = renderer.render_data(raw_profile_data)
except CompilationException as exc:
except CompilationError as exc:
raise DbtProfileError(str(exc)) from exc
return target_name, profile_data

View File

@@ -16,19 +16,19 @@ import hashlib
import os
from dbt import flags, deprecations
from dbt.clients.system import resolve_path_from_base
from dbt.clients.system import path_exists
from dbt.clients.system import load_file_contents
from dbt.clients.system import path_exists, resolve_path_from_base, load_file_contents
from dbt.clients.yaml_helper import load_yaml_text
from dbt.contracts.connection import QueryComment
from dbt.exceptions import DbtProjectError
from dbt.exceptions import SemverException
from dbt.exceptions import validator_error_message
from dbt.exceptions import RuntimeException
from dbt.exceptions import (
DbtProjectError,
SemverError,
ProjectContractBrokenError,
ProjectContractError,
DbtRuntimeError,
)
from dbt.graph import SelectionSpec
from dbt.helper_types import NoValue
from dbt.semver import VersionSpecifier
from dbt.semver import versions_compatible
from dbt.semver import VersionSpecifier, versions_compatible
from dbt.version import get_installed_version
from dbt.utils import MultiDict
from dbt.node_types import NodeType
@@ -219,7 +219,7 @@ def _get_required_version(
try:
dbt_version = _parse_versions(dbt_raw_version)
except SemverException as e:
except SemverError as e:
raise DbtProjectError(str(e)) from e
if verify_version:
@@ -325,7 +325,7 @@ class PartialProject(RenderComponents):
ProjectContract.validate(rendered.project_dict)
cfg = ProjectContract.from_dict(rendered.project_dict)
except ValidationError as e:
raise DbtProjectError(validator_error_message(e)) from e
raise ProjectContractError(e) from e
# name/version are required in the Project definition, so we can assume
# they are present
name = cfg.name
@@ -642,7 +642,7 @@ class Project:
try:
ProjectContract.validate(self.to_project_config())
except ValidationError as e:
raise DbtProjectError(validator_error_message(e)) from e
raise ProjectContractBrokenError(e) from e
@classmethod
def partial_load(cls, project_root: str, *, verify_version: bool = False) -> PartialProject:
@@ -667,7 +667,7 @@ class Project:
def get_selector(self, name: str) -> Union[SelectionSpec, bool]:
if name not in self.selectors:
raise RuntimeException(
raise DbtRuntimeError(
f"Could not find selector named {name}, expected one of {list(self.selectors)}"
)
return self.selectors[name]["definition"]

View File

@@ -8,7 +8,7 @@ from dbt.context.target import TargetContext
from dbt.context.secret import SecretContext, SECRET_PLACEHOLDER
from dbt.context.base import BaseContext
from dbt.contracts.connection import HasCredentials
from dbt.exceptions import DbtProjectError, CompilationException, RecursionException
from dbt.exceptions import DbtProjectError, CompilationError, RecursionError
from dbt.utils import deep_map_render
@@ -40,14 +40,14 @@ class BaseRenderer:
try:
with catch_jinja():
return get_rendered(value, self.context, native=True)
except CompilationException as exc:
except CompilationError as exc:
msg = f"Could not render {value}: {exc.msg}"
raise CompilationException(msg) from exc
raise CompilationError(msg) from exc
def render_data(self, data: Dict[str, Any]) -> Dict[str, Any]:
try:
return deep_map_render(self.render_entry, data)
except RecursionException:
except RecursionError:
raise DbtProjectError(
f"Cycle detected: {self.name} input has a reference to itself", project=data
)
@@ -159,7 +159,8 @@ class DbtProjectYamlRenderer(BaseRenderer):
if first in {"seeds", "models", "snapshots", "tests"}:
keypath_parts = {(k.lstrip("+ ") if isinstance(k, str) else k) for k in keypath}
# model-level hooks
if "pre-hook" in keypath_parts or "post-hook" in keypath_parts:
late_rendered_hooks = {"pre-hook", "post-hook", "pre_hook", "post_hook"}
if keypath_parts.intersection(late_rendered_hooks):
return False
return True

View File

@@ -25,10 +25,11 @@ from dbt.contracts.project import Configuration, UserConfig
from dbt.contracts.relation import ComponentName
from dbt.dataclass_schema import ValidationError
from dbt.exceptions import (
ConfigContractBrokenError,
DbtProjectError,
RuntimeException,
raise_compiler_error,
validator_error_message,
NonUniquePackageNameError,
DbtRuntimeError,
UninstalledPackagesFoundError,
)
from dbt.events.functions import warn_or_error
from dbt.events.types import UnusedResourceConfigPath
@@ -186,7 +187,7 @@ class RuntimeConfig(Project, Profile, AdapterRequiredConfig):
try:
Configuration.validate(self.serialize())
except ValidationError as e:
raise DbtProjectError(validator_error_message(e)) from e
raise ConfigContractBrokenError(e) from e
@classmethod
def _get_rendered_profile(
@@ -257,7 +258,7 @@ class RuntimeConfig(Project, Profile, AdapterRequiredConfig):
:param args: The arguments as parsed from the cli.
:raises DbtProjectError: If the project is invalid or missing.
:raises DbtProfileError: If the profile is invalid or missing.
:raises ValidationException: If the cli variables are invalid.
:raises DbtValidationError: If the cli variables are invalid.
"""
project, profile = cls.collect_parts(args)
@@ -352,22 +353,15 @@ class RuntimeConfig(Project, Profile, AdapterRequiredConfig):
count_packages_specified = len(self.packages.packages) # type: ignore
count_packages_installed = len(tuple(self._get_project_directories()))
if count_packages_specified > count_packages_installed:
raise_compiler_error(
f"dbt found {count_packages_specified} package(s) "
f"specified in packages.yml, but only "
f"{count_packages_installed} package(s) installed "
f'in {self.packages_install_path}. Run "dbt deps" to '
f"install package dependencies."
raise UninstalledPackagesFoundError(
count_packages_specified,
count_packages_installed,
self.packages_install_path,
)
project_paths = itertools.chain(internal_packages, self._get_project_directories())
for project_name, project in self.load_projects(project_paths):
if project_name in all_projects:
raise_compiler_error(
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."
)
raise NonUniquePackageNameError(project_name)
all_projects[project_name] = project
self.dependencies = all_projects
return self.dependencies
@@ -432,7 +426,7 @@ class UnsetProfile(Profile):
def __getattribute__(self, name):
if name in {"profile_name", "target_name", "threads"}:
raise RuntimeException(f'Error: disallowed attribute "{name}" - no profile!')
raise DbtRuntimeError(f'Error: disallowed attribute "{name}" - no profile!')
return Profile.__getattribute__(self, name)
@@ -459,7 +453,7 @@ class UnsetProfileConfig(RuntimeConfig):
def __getattribute__(self, name):
# Override __getattribute__ to check that the attribute isn't 'banned'.
if name in {"profile_name", "target_name"}:
raise RuntimeException(f'Error: disallowed attribute "{name}" - no profile!')
raise DbtRuntimeError(f'Error: disallowed attribute "{name}" - no profile!')
# avoid every attribute access triggering infinite recursion
return RuntimeConfig.__getattribute__(self, name)
@@ -608,7 +602,7 @@ class UnsetProfileConfig(RuntimeConfig):
:param args: The arguments as parsed from the cli.
:raises DbtProjectError: If the project is invalid or missing.
:raises DbtProfileError: If the profile is invalid or missing.
:raises ValidationException: If the cli variables are invalid.
:raises DbtValidationError: If the cli variables are invalid.
"""
project, profile = cls.collect_parts(args)

View File

@@ -12,7 +12,7 @@ from dbt.clients.system import (
resolve_path_from_base,
)
from dbt.contracts.selection import SelectorFile
from dbt.exceptions import DbtSelectorsError, RuntimeException
from dbt.exceptions import DbtSelectorsError, DbtRuntimeError
from dbt.graph import parse_from_selectors_definition, SelectionSpec
from dbt.graph.selector_spec import SelectionCriteria
@@ -46,7 +46,7 @@ class SelectorConfig(Dict[str, Dict[str, Union[SelectionSpec, bool]]]):
f"yaml-selectors",
result_type="invalid_selector",
) from exc
except RuntimeException as exc:
except DbtRuntimeError as exc:
raise DbtSelectorsError(
f"Could not read selector file data: {exc}",
result_type="invalid_selector",
@@ -62,7 +62,7 @@ class SelectorConfig(Dict[str, Dict[str, Union[SelectionSpec, bool]]]):
) -> "SelectorConfig":
try:
rendered = renderer.render_data(data)
except (ValidationError, RuntimeException) as exc:
except (ValidationError, DbtRuntimeError) as exc:
raise DbtSelectorsError(
f"Could not render selector data: {exc}",
result_type="invalid_selector",
@@ -77,7 +77,7 @@ class SelectorConfig(Dict[str, Dict[str, Union[SelectionSpec, bool]]]):
) -> "SelectorConfig":
try:
data = load_yaml_text(load_file_contents(str(path)))
except (ValidationError, RuntimeException) as exc:
except (ValidationError, DbtRuntimeError) as exc:
raise DbtSelectorsError(
f"Could not read selector file: {exc}",
result_type="invalid_selector",

View File

@@ -8,24 +8,24 @@ from dbt.clients import yaml_helper
from dbt.config import Profile, Project, read_user_config
from dbt.config.renderer import DbtProjectYamlRenderer, ProfileRenderer
from dbt.events.functions import fire_event
from dbt.events.types import InvalidVarsYAML
from dbt.exceptions import ValidationException, raise_compiler_error
from dbt.events.types import InvalidOptionYAML
from dbt.exceptions import DbtValidationError, OptionNotYamlDictError
def parse_cli_vars(var_string: str) -> Dict[str, Any]:
return parse_cli_yaml_string(var_string, "vars")
def parse_cli_yaml_string(var_string: str, cli_option_name: str) -> Dict[str, Any]:
try:
cli_vars = yaml_helper.load_yaml_text(var_string)
var_type = type(cli_vars)
if var_type is dict:
return cli_vars
else:
type_name = var_type.__name__
raise_compiler_error(
"The --vars argument must be a YAML dictionary, but was "
"of type '{}'".format(type_name)
)
except ValidationException:
fire_event(InvalidVarsYAML())
raise OptionNotYamlDictError(var_type, cli_option_name)
except DbtValidationError:
fire_event(InvalidOptionYAML(option_name=cli_option_name))
raise

View File

@@ -8,16 +8,18 @@ from dbt import utils
from dbt.clients.jinja import get_rendered
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.contracts.graph.compiled import CompiledResource
from dbt.contracts.graph.nodes import Resource
from dbt.exceptions import (
CompilationException,
SecretEnvVarLocationError,
EnvVarMissingError,
MacroReturn,
raise_compiler_error,
raise_parsing_error,
disallow_secret_env_var,
RequiredVarNotFoundError,
SetStrictWrongTypeError,
ZipStrictWrongTypeError,
)
from dbt.events.functions import fire_event, get_invocation_id
from dbt.events.types import JinjaLogInfo, JinjaLogDebug
from dbt.events.contextvars import get_node_info
from dbt.version import __version__ as dbt_version
# These modules are added to the context. Consider alternative
@@ -127,18 +129,17 @@ class ContextMeta(type):
class Var:
UndefinedVarError = "Required var '{}' not found in config:\nVars supplied to {} = {}"
_VAR_NOTSET = object()
def __init__(
self,
context: Mapping[str, Any],
cli_vars: Mapping[str, Any],
node: Optional[CompiledResource] = None,
node: Optional[Resource] = None,
) -> None:
self._context: Mapping[str, Any] = context
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()
def _generate_merged(self) -> Mapping[str, Any]:
@@ -152,10 +153,7 @@ class Var:
return "<Configuration>"
def get_missing_var(self, var_name):
dct = {k: self._merged[k] for k in self._merged}
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)
raise RequiredVarNotFoundError(var_name, self._merged, self._node)
def has_var(self, var_name: str):
return var_name in self._merged
@@ -299,7 +297,7 @@ class BaseContext(metaclass=ContextMeta):
"""
return_value = None
if var.startswith(SECRET_ENV_PREFIX):
disallow_secret_env_var(var)
raise SecretEnvVarLocationError(var)
if var in os.environ:
return_value = os.environ[var]
elif default is not None:
@@ -314,8 +312,7 @@ class BaseContext(metaclass=ContextMeta):
return return_value
else:
msg = f"Env var required but not provided: '{var}'"
raise_parsing_error(msg)
raise EnvVarMissingError(var)
if os.environ.get("DBT_MACRO_DEBUGGING"):
@@ -496,7 +493,7 @@ class BaseContext(metaclass=ContextMeta):
try:
return set(value)
except TypeError as e:
raise CompilationException(e)
raise SetStrictWrongTypeError(e)
@contextmember("zip")
@staticmethod
@@ -540,7 +537,7 @@ class BaseContext(metaclass=ContextMeta):
try:
return zip(*args)
except TypeError as e:
raise CompilationException(e)
raise ZipStrictWrongTypeError(e)
@contextmember
@staticmethod
@@ -558,9 +555,9 @@ class BaseContext(metaclass=ContextMeta):
{% endmacro %}"
"""
if info:
fire_event(JinjaLogInfo(msg=msg))
fire_event(JinjaLogInfo(msg=msg, node_info=get_node_info()))
else:
fire_event(JinjaLogDebug(msg=msg))
fire_event(JinjaLogDebug(msg=msg, node_info=get_node_info()))
return ""
@contextproperty
@@ -637,9 +634,8 @@ class BaseContext(metaclass=ContextMeta):
{% endif %}
This supports all flags defined in flags submodule (core/dbt/flags.py)
TODO: Replace with object that provides read-only access to flag values
"""
return flags
return flags.get_flag_obj()
@contextmember
@staticmethod

View File

@@ -8,7 +8,7 @@ from dbt.utils import MultiDict
from dbt.context.base import contextproperty, contextmember, Var
from dbt.context.target import TargetContext
from dbt.exceptions import raise_parsing_error, disallow_secret_env_var
from dbt.exceptions import EnvVarMissingError, SecretEnvVarLocationError
class ConfiguredContext(TargetContext):
@@ -86,7 +86,7 @@ class SchemaYamlContext(ConfiguredContext):
def env_var(self, var: str, default: Optional[str] = None) -> str:
return_value = None
if var.startswith(SECRET_ENV_PREFIX):
disallow_secret_env_var(var)
raise SecretEnvVarLocationError(var)
if var in os.environ:
return_value = os.environ[var]
elif default is not None:
@@ -104,8 +104,7 @@ class SchemaYamlContext(ConfiguredContext):
return return_value
else:
msg = f"Env var required but not provided: '{var}'"
raise_parsing_error(msg)
raise EnvVarMissingError(var)
class MacroResolvingContext(ConfiguredContext):

View File

@@ -5,7 +5,7 @@ from typing import List, Iterator, Dict, Any, TypeVar, Generic
from dbt.config import RuntimeConfig, Project, IsFQNResource
from dbt.contracts.graph.model_config import BaseConfig, get_config_for, _listify
from dbt.exceptions import InternalException
from dbt.exceptions import DbtInternalError
from dbt.node_types import NodeType
from dbt.utils import fqn_search
@@ -89,7 +89,7 @@ class BaseContextConfigGenerator(Generic[T]):
return self._active_project
dependencies = self._active_project.load_dependencies()
if project_name not in dependencies:
raise InternalException(
raise DbtInternalError(
f"Project name {project_name} not found in dependencies "
f"(found {list(dependencies)})"
)
@@ -287,14 +287,14 @@ class ContextConfig:
elif k in BaseConfig.mergebehavior["update"]:
if not isinstance(v, dict):
raise InternalException(f"expected dict, got {v}")
raise DbtInternalError(f"expected dict, got {v}")
if k in config_call_dict and isinstance(config_call_dict[k], dict):
config_call_dict[k].update(v)
else:
config_call_dict[k] = v
elif k in BaseConfig.mergebehavior["dict_key_append"]:
if not isinstance(v, dict):
raise InternalException(f"expected dict, got {v}")
raise DbtInternalError(f"expected dict, got {v}")
if k in config_call_dict: # should always be a dict
for key, value in v.items():
extend = False

View File

@@ -1,13 +1,12 @@
from typing import Any, Dict, Union
from dbt.exceptions import (
doc_invalid_args,
doc_target_not_found,
DocTargetNotFoundError,
DocArgsError,
)
from dbt.config.runtime import RuntimeConfig
from dbt.contracts.graph.compiled import CompileResultNode
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.configured import SchemaYamlContext
@@ -17,7 +16,7 @@ class DocsRuntimeContext(SchemaYamlContext):
def __init__(
self,
config: RuntimeConfig,
node: Union[ParsedMacro, CompileResultNode],
node: Union[Macro, ResultNode],
manifest: Manifest,
current_project: str,
) -> None:
@@ -53,9 +52,9 @@ class DocsRuntimeContext(SchemaYamlContext):
elif len(args) == 2:
doc_package_name, doc_name = args
else:
doc_invalid_args(self.node, args)
raise DocArgsError(self.node, args)
# ParsedDocumentation
# Documentation
target_doc = self.manifest.resolve_doc(
doc_name,
doc_package_name,
@@ -69,7 +68,9 @@ class DocsRuntimeContext(SchemaYamlContext):
# TODO CT-211
source_file.add_node(self.node.unique_id) # type: ignore[union-attr]
else:
doc_target_not_found(self.node, doc_name, doc_package_name)
raise DocTargetNotFoundError(
node=self.node, target_doc_name=doc_name, target_doc_package=doc_package_name
)
return target_doc.block_contents

View File

@@ -0,0 +1,144 @@
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 (
DbtRuntimeError,
MissingConfigError,
MissingMaterializationError,
MissingRelationError,
AmbiguousAliasError,
AmbiguousCatalogMatchError,
CacheInconsistencyError,
DataclassNotDictError,
CompilationError,
DbtDatabaseError,
DependencyNotFoundError,
DependencyError,
DuplicatePatchPathError,
DuplicateResourceNameError,
PropertyYMLError,
NotImplementedError,
RelationWrongTypeError,
)
def warn(msg, node=None):
warn_or_error(JinjaLogWarning(msg=msg), node=node)
return ""
def missing_config(model, name) -> NoReturn:
raise MissingConfigError(unique_id=model.unique_id, name=name)
def missing_materialization(model, adapter_type) -> NoReturn:
raise MissingMaterializationError(
materialization=model.config.materialized, adapter_type=adapter_type
)
def missing_relation(relation, model=None) -> NoReturn:
raise MissingRelationError(relation, model)
def raise_ambiguous_alias(node_1, node_2, duped_name=None) -> NoReturn:
raise AmbiguousAliasError(node_1, node_2, duped_name)
def raise_ambiguous_catalog_match(unique_id, match_1, match_2) -> NoReturn:
raise AmbiguousCatalogMatchError(unique_id, match_1, match_2)
def raise_cache_inconsistent(message) -> NoReturn:
raise CacheInconsistencyError(message)
def raise_dataclass_not_dict(obj) -> NoReturn:
raise DataclassNotDictError(obj)
def raise_compiler_error(msg, node=None) -> NoReturn:
raise CompilationError(msg, node)
def raise_database_error(msg, node=None) -> NoReturn:
raise DbtDatabaseError(msg, node)
def raise_dep_not_found(node, node_description, required_pkg) -> NoReturn:
raise DependencyNotFoundError(node, node_description, required_pkg)
def raise_dependency_error(msg) -> NoReturn:
raise DependencyError(scrub_secrets(msg, env_secrets()))
def raise_duplicate_patch_name(patch_1, existing_patch_path) -> NoReturn:
raise DuplicatePatchPathError(patch_1, existing_patch_path)
def raise_duplicate_resource_name(node_1, node_2) -> NoReturn:
raise DuplicateResourceNameError(node_1, node_2)
def raise_invalid_property_yml_version(path, issue) -> NoReturn:
raise PropertyYMLError(path, issue)
def raise_not_implemented(msg) -> NoReturn:
raise NotImplementedError(msg)
def relation_wrong_type(relation, expected_type, model=None) -> NoReturn:
raise RelationWrongTypeError(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 DbtRuntimeError 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()}

View File

@@ -1,10 +1,10 @@
from typing import Dict, MutableMapping, Optional
from dbt.contracts.graph.parsed import ParsedMacro
from dbt.exceptions import raise_duplicate_macro_name, raise_compiler_error
from dbt.contracts.graph.nodes import Macro
from dbt.exceptions import DuplicateMacroNameError, PackageNotFoundForMacroError
from dbt.include.global_project import PROJECT_NAME as GLOBAL_PROJECT_NAME
from dbt.clients.jinja import MacroGenerator
MacroNamespace = Dict[str, ParsedMacro]
MacroNamespace = Dict[str, Macro]
# This class builds the MacroResolver by adding macros
@@ -21,7 +21,7 @@ MacroNamespace = Dict[str, ParsedMacro]
class MacroResolver:
def __init__(
self,
macros: MutableMapping[str, ParsedMacro],
macros: MutableMapping[str, Macro],
root_project_name: str,
internal_package_names,
) -> None:
@@ -77,7 +77,7 @@ class MacroResolver:
def _add_macro_to(
self,
package_namespaces: Dict[str, MacroNamespace],
macro: ParsedMacro,
macro: Macro,
):
if macro.package_name in package_namespaces:
namespace = package_namespaces[macro.package_name]
@@ -86,10 +86,10 @@ class MacroResolver:
package_namespaces[macro.package_name] = namespace
if macro.name in namespace:
raise_duplicate_macro_name(macro, macro, macro.package_name)
raise DuplicateMacroNameError(macro, macro, macro.package_name)
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
# internal macros (from plugins) will be processed separately from
@@ -187,7 +187,7 @@ class TestMacroNamespace:
elif package_name in self.macro_resolver.packages:
macro = self.macro_resolver.packages[package_name].get(name)
else:
raise_compiler_error(f"Could not find package '{package_name}'")
raise PackageNotFoundForMacroError(package_name)
if not macro:
return None
macro_func = MacroGenerator(macro, self.ctx, self.node, self.thread_ctx)

View File

@@ -1,9 +1,9 @@
from typing import Any, Dict, Iterable, Union, Optional, List, Iterator, Mapping, Set
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.exceptions import raise_duplicate_macro_name, raise_compiler_error
from dbt.exceptions import DuplicateMacroNameError, PackageNotFoundForMacroError
FlatNamespace = Dict[str, MacroGenerator]
@@ -75,7 +75,7 @@ class MacroNamespace(Mapping):
elif package_name in self.packages:
return self.packages[package_name].get(name)
else:
raise_compiler_error(f"Could not find package '{package_name}'")
raise PackageNotFoundForMacroError(package_name)
# This class builds the MacroNamespace by adding macros to
@@ -112,7 +112,7 @@ class MacroNamespaceBuilder:
def _add_macro_to(
self,
hierarchy: Dict[str, FlatNamespace],
macro: ParsedMacro,
macro: Macro,
macro_func: MacroGenerator,
):
if macro.package_name in hierarchy:
@@ -122,10 +122,10 @@ class MacroNamespaceBuilder:
hierarchy[macro.package_name] = namespace
if macro.name in namespace:
raise_duplicate_macro_name(macro_func.macro, macro, macro.package_name)
raise DuplicateMacroNameError(macro_func.macro, macro, macro.package_name)
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
# MacroGenerator is in clients/jinja.py
@@ -147,13 +147,11 @@ class MacroNamespaceBuilder:
elif macro.package_name == self.root_package:
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:
self.add_macro(macro, ctx)
def build_namespace(
self, macros: Iterable[ParsedMacro], ctx: Dict[str, Any]
) -> MacroNamespace:
def build_namespace(self, macros: Iterable[Macro], ctx: Dict[str, Any]) -> MacroNamespace:
self.add_macros(macros, ctx)
# Iterate in reverse-order and overwrite: the packages that are first

View File

@@ -19,45 +19,50 @@ from dbt.adapters.factory import get_adapter, get_adapter_package_names, get_ada
from dbt.clients import agate_helper
from dbt.clients.jinja import get_rendered, MacroGenerator, MacroStack
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.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 .macros import MacroNamespaceBuilder, MacroNamespace
from .manifest import ManifestContext
from dbt.context.macros import MacroNamespaceBuilder, MacroNamespace
from dbt.context.manifest import ManifestContext
from dbt.contracts.connection import AdapterResponse
from dbt.contracts.graph.manifest import Manifest, Disabled
from dbt.contracts.graph.compiled import (
CompiledResource,
CompiledSeedNode,
from dbt.contracts.graph.nodes import (
Macro,
Exposure,
Metric,
SeedNode,
SourceDefinition,
Resource,
ManifestNode,
)
from dbt.contracts.graph.parsed import (
ParsedMacro,
ParsedExposure,
ParsedMetric,
ParsedSeedNode,
ParsedSourceDefinition,
)
from dbt.contracts.graph.metrics import MetricReference, ResolvedMetricReference
from dbt.events.functions import get_metadata_vars
from dbt.exceptions import (
CompilationException,
ParsingException,
InternalException,
ValidationException,
RuntimeException,
macro_invalid_dispatch_arg,
missing_config,
raise_compiler_error,
ref_invalid_args,
metric_invalid_args,
target_not_found,
ref_bad_context,
wrapped_exports,
raise_parsing_error,
disallow_secret_env_var,
CompilationError,
ConflictingConfigKeysError,
SecretEnvVarLocationError,
EnvVarMissingError,
DbtInternalError,
InlineModelConfigError,
NumberSourceArgsError,
PersistDocsValueTypeError,
LoadAgateTableNotSeedError,
LoadAgateTableValueError,
MacroDispatchArgError,
MacrosSourcesUnWriteableError,
MetricArgsError,
MissingConfigError,
OperationsCannotRefEphemeralNodesError,
PackageNotInDepsError,
ParsingError,
RefBadContextError,
RefArgsError,
DbtRuntimeError,
TargetNotFoundError,
DbtValidationError,
)
from dbt.config import IsFQNResource
from dbt.node_types import NodeType, ModelLanguage
@@ -139,10 +144,10 @@ class BaseDatabaseWrapper:
f'`adapter.dispatch("{suggest_macro_name}", '
f'macro_namespace="{suggest_macro_namespace}")`?'
)
raise CompilationException(msg)
raise CompilationError(msg)
if packages is not None:
raise macro_invalid_dispatch_arg(macro_name)
raise MacroDispatchArgError(macro_name)
namespace = macro_namespace
@@ -154,7 +159,7 @@ class BaseDatabaseWrapper:
search_packages = [self.config.project_name, namespace]
else:
# Not a string and not None so must be a list
raise CompilationException(
raise CompilationError(
f"In adapter.dispatch, got a list macro_namespace argument "
f'("{macro_namespace}"), but macro_namespace should be None or a string.'
)
@@ -167,8 +172,8 @@ class BaseDatabaseWrapper:
try:
# this uses the namespace from the context
macro = self._namespace.get_from_package(package_name, search_name)
except CompilationException:
# Only raise CompilationException if macro is not found in
except CompilationError:
# Only raise CompilationError if macro is not found in
# any package
macro = None
@@ -182,7 +187,7 @@ class BaseDatabaseWrapper:
searched = ", ".join(repr(a) for a in attempts)
msg = f"In dispatch: No macro named '{macro_name}' found\n Searched for: {searched}"
raise CompilationException(msg)
raise CompilationError(msg)
class BaseResolver(metaclass=abc.ABCMeta):
@@ -218,12 +223,12 @@ class BaseRefResolver(BaseResolver):
def validate_args(self, name: str, package: Optional[str]):
if not isinstance(name, str):
raise CompilationException(
raise CompilationError(
f"The name argument to ref() must be a string, got {type(name)}"
)
if package is not None and not isinstance(package, str):
raise CompilationException(
raise CompilationError(
f"The package argument to ref() must be a string or None, got {type(package)}"
)
@@ -236,7 +241,7 @@ class BaseRefResolver(BaseResolver):
elif len(args) == 2:
package, name = args
else:
ref_invalid_args(self.model, args)
raise RefArgsError(node=self.model, args=args)
self.validate_args(name, package)
return self.resolve(name, package)
@@ -248,21 +253,19 @@ class BaseSourceResolver(BaseResolver):
def validate_args(self, source_name: str, table_name: str):
if not isinstance(source_name, str):
raise CompilationException(
raise CompilationError(
f"The source name (first) argument to source() must be a "
f"string, got {type(source_name)}"
)
if not isinstance(table_name, str):
raise CompilationException(
raise CompilationError(
f"The table name (second) argument to source() must be a "
f"string, got {type(table_name)}"
)
def __call__(self, *args: str) -> RelationProxy:
if len(args) != 2:
raise_compiler_error(
f"source() takes exactly two arguments ({len(args)} given)", self.model
)
raise NumberSourceArgsError(args, node=self.model)
self.validate_args(args[0], args[1])
return self.resolve(args[0], args[1])
@@ -279,12 +282,12 @@ class BaseMetricResolver(BaseResolver):
def validate_args(self, name: str, package: Optional[str]):
if not isinstance(name, str):
raise CompilationException(
raise CompilationError(
f"The name argument to metric() must be a string, got {type(name)}"
)
if package is not None and not isinstance(package, str):
raise CompilationException(
raise CompilationError(
f"The package argument to metric() must be a string or None, got {type(package)}"
)
@@ -297,7 +300,7 @@ class BaseMetricResolver(BaseResolver):
elif len(args) == 2:
package, name = args
else:
metric_invalid_args(self.model, args)
raise MetricArgsError(node=self.model, args=args)
self.validate_args(name, package)
return self.resolve(name, package)
@@ -318,12 +321,7 @@ class ParseConfigObject(Config):
if oldkey in config:
newkey = oldkey.replace("_", "-")
if newkey in config:
raise_compiler_error(
'Invalid config, has conflicting keys "{}" and "{}"'.format(
oldkey, newkey
),
self.model,
)
raise ConflictingConfigKeysError(oldkey, newkey, node=self.model)
config[newkey] = config.pop(oldkey)
return config
@@ -333,14 +331,14 @@ class ParseConfigObject(Config):
elif len(args) == 0 and len(kwargs) > 0:
opts = kwargs
else:
raise_compiler_error("Invalid inline model config", self.model)
raise InlineModelConfigError(node=self.model)
opts = self._transform_config(opts)
# it's ok to have a parse context with no context config, but you must
# not call it!
if self.context_config is None:
raise RuntimeException("At parse time, did not receive a context config")
raise DbtRuntimeError("At parse time, did not receive a context config")
self.context_config.add_config_call(opts)
return ""
@@ -381,7 +379,7 @@ class RuntimeConfigObject(Config):
else:
result = self.model.config.get(name, default)
if result is _MISSING:
missing_config(self.model, name)
raise MissingConfigError(unique_id=self.model.unique_id, name=name)
return result
def require(self, name, validator=None):
@@ -403,20 +401,14 @@ class RuntimeConfigObject(Config):
def persist_relation_docs(self) -> bool:
persist_docs = self.get("persist_docs", default={})
if not isinstance(persist_docs, dict):
raise_compiler_error(
f"Invalid value provided for 'persist_docs'. Expected dict "
f"but received {type(persist_docs)}"
)
raise PersistDocsValueTypeError(persist_docs)
return persist_docs.get("relation", False)
def persist_column_docs(self) -> bool:
persist_docs = self.get("persist_docs", default={})
if not isinstance(persist_docs, dict):
raise_compiler_error(
f"Invalid value provided for 'persist_docs'. Expected dict "
f"but received {type(persist_docs)}"
)
raise PersistDocsValueTypeError(persist_docs)
return persist_docs.get("columns", False)
@@ -475,7 +467,7 @@ class RuntimeRefResolver(BaseRefResolver):
)
if target_model is None or isinstance(target_model, Disabled):
target_not_found(
raise TargetNotFoundError(
node=self.model,
target_name=target_name,
target_kind="node",
@@ -497,7 +489,7 @@ class RuntimeRefResolver(BaseRefResolver):
) -> None:
if resolved.unique_id not in self.model.depends_on.nodes:
args = self._repack_args(target_name, target_package)
ref_bad_context(self.model, args)
raise RefBadContextError(node=self.model, args=args)
class OperationRefResolver(RuntimeRefResolver):
@@ -512,13 +504,8 @@ class OperationRefResolver(RuntimeRefResolver):
def create_relation(self, target_model: ManifestNode, name: str) -> RelationProxy:
if target_model.is_ephemeral_model:
# In operations, we can't ref() ephemeral nodes, because
# ParsedMacros do not support set_cte
raise_compiler_error(
"Operations can not ref() ephemeral nodes, but {} is ephemeral".format(
target_model.name
),
self.model,
)
# Macros do not support set_cte
raise OperationsCannotRefEphemeralNodesError(target_model.name, node=self.model)
else:
return super().create_relation(target_model, name)
@@ -541,7 +528,7 @@ class RuntimeSourceResolver(BaseSourceResolver):
)
if target_source is None or isinstance(target_source, Disabled):
target_not_found(
raise TargetNotFoundError(
node=self.model,
target_name=f"{source_name}.{table_name}",
target_kind="source",
@@ -568,7 +555,7 @@ class RuntimeMetricResolver(BaseMetricResolver):
)
if target_metric is None or isinstance(target_metric, Disabled):
target_not_found(
raise TargetNotFoundError(
node=self.model,
target_name=target_name,
target_kind="metric",
@@ -584,9 +571,9 @@ class ModelConfiguredVar(Var):
self,
context: Dict[str, Any],
config: RuntimeConfig,
node: CompiledResource,
node: Resource,
) -> None:
self._node: CompiledResource
self._node: Resource
self._config: RuntimeConfig = config
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 not in dependencies:
# I don't think this is actually reachable
raise_compiler_error(f"Node package named {package_name} not found!", self._node)
raise PackageNotInDepsError(package_name, node=self._node)
yield dependencies[package_name]
yield self._config
@@ -687,10 +674,10 @@ class ProviderContext(ManifestContext):
context_config: Optional[ContextConfig],
) -> None:
if provider is None:
raise InternalException(f"Invalid provider given to context: {provider}")
raise DbtInternalError(f"Invalid provider given to context: {provider}")
# mypy appeasement - we know it'll be a RuntimeConfig
self.config: RuntimeConfig
self.model: Union[ParsedMacro, ManifestNode] = model
self.model: Union[Macro, ManifestNode] = model
super().__init__(config, manifest, model.package_name)
self.sql_results: Dict[str, AttrDict] = {}
self.context_config: Optional[ContextConfig] = context_config
@@ -764,7 +751,7 @@ class ProviderContext(ManifestContext):
return
elif value == arg:
return
raise ValidationException(
raise DbtValidationError(
'Expected value "{}" to be one of {}'.format(value, ",".join(map(str, args)))
)
@@ -779,8 +766,8 @@ class ProviderContext(ManifestContext):
@contextmember
def write(self, payload: str) -> str:
# macros/source defs aren't 'writeable'.
if isinstance(self.model, (ParsedMacro, ParsedSourceDefinition)):
raise_compiler_error('cannot "write" macros or sources')
if isinstance(self.model, (Macro, SourceDefinition)):
raise MacrosSourcesUnWriteableError(node=self.model)
self.model.build_path = self.model.write_node(self.config.target_path, "run", payload)
return ""
@@ -795,21 +782,19 @@ class ProviderContext(ManifestContext):
try:
return func(*args, **kwargs)
except Exception:
raise_compiler_error(message_if_exception, self.model)
raise CompilationError(message_if_exception, self.model)
@contextmember
def load_agate_table(self) -> agate.Table:
if not isinstance(self.model, (ParsedSeedNode, CompiledSeedNode)):
raise_compiler_error(
"can only load_agate_table for seeds (got a {})".format(self.model.resource_type)
)
if not isinstance(self.model, SeedNode):
raise LoadAgateTableNotSeedError(self.model.resource_type, node=self.model)
assert self.model.root_path
path = os.path.join(self.model.root_path, self.model.original_file_path)
column_types = self.model.config.column_types
try:
table = agate_helper.from_csv(path, text_columns=column_types)
except ValueError as e:
raise_compiler_error(str(e))
raise LoadAgateTableValueError(e, node=self.model)
table.original_abspath = os.path.abspath(path)
return table
@@ -1200,7 +1185,7 @@ class ProviderContext(ManifestContext):
"https://docs.getdbt.com/reference/dbt-jinja-functions/dispatch)"
" adapter_macro was called for: {macro_name}".format(macro_name=name)
)
raise CompilationException(msg)
raise CompilationError(msg)
@contextmember
def env_var(self, var: str, default: Optional[str] = None) -> str:
@@ -1211,7 +1196,7 @@ class ProviderContext(ManifestContext):
"""
return_value = None
if var.startswith(SECRET_ENV_PREFIX):
disallow_secret_env_var(var)
raise SecretEnvVarLocationError(var)
if var in os.environ:
return_value = os.environ[var]
elif default is not None:
@@ -1220,7 +1205,13 @@ class ProviderContext(ManifestContext):
if return_value is not None:
# 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 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
# 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
@@ -1238,8 +1229,7 @@ class ProviderContext(ManifestContext):
source_file.env_vars.append(var) # type: ignore[union-attr]
return return_value
else:
msg = f"Env var required but not provided: '{var}'"
raise_parsing_error(msg)
raise EnvVarMissingError(var)
@contextproperty
def selected_resources(self) -> List[str]:
@@ -1258,7 +1248,7 @@ class ProviderContext(ManifestContext):
and self.context_macro_stack.call_stack[1] == "macro.dbt.statement"
and "materialization" in self.context_macro_stack.call_stack[0]
):
raise RuntimeException(
raise DbtRuntimeError(
f"submit_python_job is not intended to be called here, at model {parsed_model['alias']}, with macro call_stack {self.context_macro_stack.call_stack}."
)
return self.adapter.submit_python_job(parsed_model, compiled_code)
@@ -1275,7 +1265,7 @@ class MacroContext(ProviderContext):
def __init__(
self,
model: ParsedMacro,
model: Macro,
config: RuntimeConfig,
manifest: Manifest,
provider: Provider,
@@ -1390,7 +1380,7 @@ def generate_parser_model_context(
def generate_generate_name_macro_context(
macro: ParsedMacro,
macro: Macro,
config: RuntimeConfig,
manifest: Manifest,
) -> Dict[str, Any]:
@@ -1408,7 +1398,7 @@ def generate_runtime_model_context(
def generate_runtime_macro_context(
macro: ParsedMacro,
macro: Macro,
config: RuntimeConfig,
manifest: Manifest,
package_name: Optional[str],
@@ -1420,7 +1410,7 @@ def generate_runtime_macro_context(
class ExposureRefResolver(BaseResolver):
def __call__(self, *args) -> str:
if len(args) not in (1, 2):
ref_invalid_args(self.model, args)
raise RefArgsError(node=self.model, args=args)
self.model.refs.append(list(args))
return ""
@@ -1428,9 +1418,7 @@ class ExposureRefResolver(BaseResolver):
class ExposureSourceResolver(BaseResolver):
def __call__(self, *args) -> str:
if len(args) != 2:
raise_compiler_error(
f"source() takes exactly two arguments ({len(args)} given)", self.model
)
raise NumberSourceArgsError(args, node=self.model)
self.model.sources.append(list(args))
return ""
@@ -1438,13 +1426,13 @@ class ExposureSourceResolver(BaseResolver):
class ExposureMetricResolver(BaseResolver):
def __call__(self, *args) -> str:
if len(args) not in (1, 2):
metric_invalid_args(self.model, args)
raise MetricArgsError(node=self.model, args=args)
self.model.metrics.append(list(args))
return ""
def generate_parse_exposure(
exposure: ParsedExposure,
exposure: Exposure,
config: RuntimeConfig,
manifest: Manifest,
package_name: str,
@@ -1480,21 +1468,21 @@ class MetricRefResolver(BaseResolver):
elif len(args) == 2:
package, name = args
else:
ref_invalid_args(self.model, args)
raise RefArgsError(node=self.model, args=args)
self.validate_args(name, package)
self.model.refs.append(list(args))
return ""
def validate_args(self, name, package):
if not isinstance(name, str):
raise ParsingException(
raise ParsingError(
f"In a metrics section in {self.model.original_file_path} "
"the name argument to ref() must be a string"
)
def generate_parse_metrics(
metric: ParsedMetric,
metric: Metric,
config: RuntimeConfig,
manifest: Manifest,
package_name: str,
@@ -1570,7 +1558,7 @@ class TestContext(ProviderContext):
def env_var(self, var: str, default: Optional[str] = None) -> str:
return_value = None
if var.startswith(SECRET_ENV_PREFIX):
disallow_secret_env_var(var)
raise SecretEnvVarLocationError(var)
if var in os.environ:
return_value = os.environ[var]
elif default is not None:
@@ -1596,8 +1584,7 @@ class TestContext(ProviderContext):
source_file.add_env_var(var, yaml_key, name) # type: ignore[union-attr]
return return_value
else:
msg = f"Env var required but not provided: '{var}'"
raise_parsing_error(msg)
raise EnvVarMissingError(var)
def generate_test_context(

View File

@@ -4,7 +4,7 @@ from typing import Any, Dict, Optional
from .base import BaseContext, contextmember
from dbt.constants import SECRET_ENV_PREFIX, DEFAULT_ENV_PLACEHOLDER
from dbt.exceptions import raise_parsing_error
from dbt.exceptions import EnvVarMissingError
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
return return_value
else:
msg = f"Env var required but not provided: '{var}'"
raise_parsing_error(msg)
raise EnvVarMissingError(var)
def generate_secret_context(cli_vars: Dict[str, Any]) -> Dict[str, Any]:

View File

@@ -12,10 +12,11 @@ from typing import (
List,
Callable,
)
from dbt.exceptions import InternalException
from dbt.exceptions import DbtInternalError
from dbt.utils import translate_aliases
from dbt.events.functions import fire_event
from dbt.events.types import NewConnectionOpening
from dbt.events.contextvars import get_node_info
from typing_extensions import Protocol
from dbt.dataclass_schema import (
dbtClassMixin,
@@ -93,7 +94,7 @@ class Connection(ExtensibleDbtClassMixin, Replaceable):
# this will actually change 'self._handle'.
self._handle.resolve(self)
except RecursionError as exc:
raise InternalException(
raise DbtInternalError(
"A connection's open() method attempted to read the handle value"
) from exc
return self._handle
@@ -112,7 +113,9 @@ class LazyHandle:
self.opener = opener
def resolve(self, connection: Connection) -> Connection:
fire_event(NewConnectionOpening(connection_state=connection.state))
fire_event(
NewConnectionOpening(connection_state=connection.state, node_info=get_node_info())
)
return self.opener(connection)

View File

@@ -1,236 +0,0 @@
from dbt.contracts.graph.parsed import (
HasTestMetadata,
ParsedNode,
ParsedAnalysisNode,
ParsedSingularTestNode,
ParsedHookNode,
ParsedModelNode,
ParsedExposure,
ParsedMetric,
ParsedResource,
ParsedRPCNode,
ParsedSqlNode,
ParsedGenericTestNode,
ParsedSeedNode,
ParsedSnapshotNode,
ParsedSourceDefinition,
SeedConfig,
TestConfig,
same_seeds,
)
from dbt.node_types import NodeType
from dbt.contracts.util import Replaceable
from dbt.dataclass_schema import dbtClassMixin
from dataclasses import dataclass, field
from typing import Optional, List, Union, Dict, Type
@dataclass
class InjectedCTE(dbtClassMixin, Replaceable):
id: str
sql: str
@dataclass
class CompiledNodeMixin(dbtClassMixin):
# this is a special mixin class to provide a required argument. If a node
# is missing a `compiled` flag entirely, it must not be a CompiledNode.
compiled: bool
@dataclass
class CompiledNode(ParsedNode, CompiledNodeMixin):
compiled_code: Optional[str] = None
extra_ctes_injected: bool = False
extra_ctes: List[InjectedCTE] = field(default_factory=list)
relation_name: Optional[str] = None
_pre_injected_sql: Optional[str] = None
def set_cte(self, cte_id: str, sql: str):
"""This is the equivalent of what self.extra_ctes[cte_id] = sql would
do if extra_ctes were an OrderedDict
"""
for cte in self.extra_ctes:
if cte.id == cte_id:
cte.sql = sql
break
else:
self.extra_ctes.append(InjectedCTE(id=cte_id, sql=sql))
def __post_serialize__(self, dct):
dct = super().__post_serialize__(dct)
if "_pre_injected_sql" in dct:
del dct["_pre_injected_sql"]
return dct
@dataclass
class CompiledAnalysisNode(CompiledNode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.Analysis]})
@dataclass
class CompiledHookNode(CompiledNode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.Operation]})
index: Optional[int] = None
@dataclass
class CompiledModelNode(CompiledNode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.Model]})
# TODO: rm?
@dataclass
class CompiledRPCNode(CompiledNode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.RPCCall]})
@dataclass
class CompiledSqlNode(CompiledNode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.SqlOperation]})
@dataclass
class CompiledSeedNode(CompiledNode):
# keep this in sync with ParsedSeedNode!
resource_type: NodeType = field(metadata={"restrict": [NodeType.Seed]})
config: SeedConfig = field(default_factory=SeedConfig)
root_path: Optional[str] = None
@property
def empty(self):
"""Seeds are never empty"""
return False
def same_body(self, other) -> bool:
return same_seeds(self, other)
@dataclass
class CompiledSnapshotNode(CompiledNode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.Snapshot]})
@dataclass
class CompiledSingularTestNode(CompiledNode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.Test]})
# Was not able to make mypy happy and keep the code working. We need to
# refactor the various configs.
config: TestConfig = field(default_factory=TestConfig) # type:ignore
@dataclass
class CompiledGenericTestNode(CompiledNode, HasTestMetadata):
# keep this in sync with ParsedGenericTestNode!
resource_type: NodeType = field(metadata={"restrict": [NodeType.Test]})
column_name: Optional[str] = None
file_key_name: Optional[str] = None
# Was not able to make mypy happy and keep the code working. We need to
# refactor the various configs.
config: TestConfig = field(default_factory=TestConfig) # type:ignore
def same_contents(self, other) -> bool:
if other is None:
return False
return self.same_config(other) and self.same_fqn(other) and True
CompiledTestNode = Union[CompiledSingularTestNode, CompiledGenericTestNode]
PARSED_TYPES: Dict[Type[CompiledNode], Type[ParsedResource]] = {
CompiledAnalysisNode: ParsedAnalysisNode,
CompiledModelNode: ParsedModelNode,
CompiledHookNode: ParsedHookNode,
CompiledRPCNode: ParsedRPCNode,
CompiledSqlNode: ParsedSqlNode,
CompiledSeedNode: ParsedSeedNode,
CompiledSnapshotNode: ParsedSnapshotNode,
CompiledSingularTestNode: ParsedSingularTestNode,
CompiledGenericTestNode: ParsedGenericTestNode,
}
COMPILED_TYPES: Dict[Type[ParsedResource], Type[CompiledNode]] = {
ParsedAnalysisNode: CompiledAnalysisNode,
ParsedModelNode: CompiledModelNode,
ParsedHookNode: CompiledHookNode,
ParsedRPCNode: CompiledRPCNode,
ParsedSqlNode: CompiledSqlNode,
ParsedSeedNode: CompiledSeedNode,
ParsedSnapshotNode: CompiledSnapshotNode,
ParsedSingularTestNode: CompiledSingularTestNode,
ParsedGenericTestNode: CompiledGenericTestNode,
}
# for some types, the compiled type is the parsed type, so make this easy
CompiledType = Union[Type[CompiledNode], Type[ParsedResource]]
CompiledResource = Union[ParsedResource, CompiledNode]
def compiled_type_for(parsed: ParsedNode) -> CompiledType:
if type(parsed) in COMPILED_TYPES:
return COMPILED_TYPES[type(parsed)]
else:
return type(parsed)
def parsed_instance_for(compiled: CompiledNode) -> ParsedResource:
cls = PARSED_TYPES.get(type(compiled))
if cls is None:
# how???
raise ValueError("invalid resource_type: {}".format(compiled.resource_type))
return cls.from_dict(compiled.to_dict(omit_none=True))
NonSourceCompiledNode = Union[
CompiledAnalysisNode,
CompiledSingularTestNode,
CompiledModelNode,
CompiledHookNode,
CompiledRPCNode,
CompiledSqlNode,
CompiledGenericTestNode,
CompiledSeedNode,
CompiledSnapshotNode,
]
NonSourceParsedNode = Union[
ParsedAnalysisNode,
ParsedSingularTestNode,
ParsedHookNode,
ParsedModelNode,
ParsedRPCNode,
ParsedSqlNode,
ParsedGenericTestNode,
ParsedSeedNode,
ParsedSnapshotNode,
]
# This is anything that can be in manifest.nodes.
ManifestNode = Union[
NonSourceCompiledNode,
NonSourceParsedNode,
]
# We allow either parsed or compiled nodes, or parsed sources, as some
# 'compile()' calls in the runner actually just return the original parsed
# node they were given.
CompileResultNode = Union[
ManifestNode,
ParsedSourceDefinition,
]
# anything that participates in the graph: sources, exposures, metrics,
# or manifest nodes
GraphMemberNode = Union[
CompileResultNode,
ParsedExposure,
ParsedMetric,
]

View File

@@ -16,44 +16,39 @@ from typing import (
TypeVar,
Callable,
Generic,
cast,
AbstractSet,
ClassVar,
)
from typing_extensions import Protocol
from uuid import UUID
from dbt.contracts.graph.compiled import (
CompileResultNode,
ManifestNode,
NonSourceCompiledNode,
GraphMemberNode,
)
from dbt.contracts.graph.parsed import (
ParsedMacro,
ParsedDocumentation,
ParsedSourceDefinition,
ParsedGenericTestNode,
ParsedExposure,
ParsedMetric,
HasUniqueID,
from dbt.contracts.graph.nodes import (
Macro,
Documentation,
SourceDefinition,
GenericTestNode,
Exposure,
Metric,
UnpatchedSourceDefinition,
ManifestNodes,
ManifestNode,
GraphMemberNode,
ResultNode,
BaseNode,
)
from dbt.contracts.graph.unparsed import SourcePatch
from dbt.contracts.files import SourceFile, SchemaSourceFile, FileHash, AnySourceFile
from dbt.contracts.util import BaseArtifactMetadata, SourceKey, ArtifactMixin, schema_version
from dbt.dataclass_schema import dbtClassMixin
from dbt.exceptions import (
CompilationException,
raise_duplicate_resource_name,
raise_compiler_error,
CompilationError,
DuplicateResourceNameError,
DuplicateMacroInPackageError,
DuplicateMaterializationNameError,
)
from dbt.helper_types import PathSet
from dbt.events.functions import fire_event
from dbt.events.types import MergedFromState
from dbt.node_types import NodeType
from dbt.ui import line_wrap_message
from dbt import flags
from dbt import tracking
import dbt.utils
@@ -96,7 +91,7 @@ class DocLookup(dbtClassMixin):
return self.perform_lookup(unique_id, manifest)
return None
def add_doc(self, doc: ParsedDocumentation):
def add_doc(self, doc: Documentation):
if doc.name not in self.storage:
self.storage[doc.name] = {}
self.storage[doc.name][doc.package_name] = doc.unique_id
@@ -105,9 +100,9 @@ class DocLookup(dbtClassMixin):
for doc in manifest.docs.values():
self.add_doc(doc)
def perform_lookup(self, unique_id: UniqueID, manifest) -> ParsedDocumentation:
def perform_lookup(self, unique_id: UniqueID, manifest) -> Documentation:
if unique_id not in manifest.docs:
raise dbt.exceptions.InternalException(
raise dbt.exceptions.DbtInternalError(
f"Doc {unique_id} found in cache but not found in manifest"
)
return manifest.docs[unique_id]
@@ -127,7 +122,7 @@ class SourceLookup(dbtClassMixin):
return self.perform_lookup(unique_id, manifest)
return None
def add_source(self, source: ParsedSourceDefinition):
def add_source(self, source: SourceDefinition):
if source.search_name not in self.storage:
self.storage[source.search_name] = {}
@@ -138,9 +133,9 @@ class SourceLookup(dbtClassMixin):
if hasattr(source, "source_name"):
self.add_source(source)
def perform_lookup(self, unique_id: UniqueID, manifest: "Manifest") -> ParsedSourceDefinition:
def perform_lookup(self, unique_id: UniqueID, manifest: "Manifest") -> SourceDefinition:
if unique_id not in manifest.sources:
raise dbt.exceptions.InternalException(
raise dbt.exceptions.DbtInternalError(
f"Source {unique_id} found in cache but not found in manifest"
)
return manifest.sources[unique_id]
@@ -178,7 +173,7 @@ class RefableLookup(dbtClassMixin):
def perform_lookup(self, unique_id: UniqueID, manifest) -> ManifestNode:
if unique_id not in manifest.nodes:
raise dbt.exceptions.InternalException(
raise dbt.exceptions.DbtInternalError(
f"Node {unique_id} found in cache but not found in manifest"
)
return manifest.nodes[unique_id]
@@ -198,7 +193,7 @@ class MetricLookup(dbtClassMixin):
return self.perform_lookup(unique_id, manifest)
return None
def add_metric(self, metric: ParsedMetric):
def add_metric(self, metric: Metric):
if metric.search_name not in self.storage:
self.storage[metric.search_name] = {}
@@ -209,9 +204,9 @@ class MetricLookup(dbtClassMixin):
if hasattr(metric, "name"):
self.add_metric(metric)
def perform_lookup(self, unique_id: UniqueID, manifest: "Manifest") -> ParsedMetric:
def perform_lookup(self, unique_id: UniqueID, manifest: "Manifest") -> Metric:
if unique_id not in manifest.metrics:
raise dbt.exceptions.InternalException(
raise dbt.exceptions.DbtInternalError(
f"Metric {unique_id} found in cache but not found in manifest"
)
return manifest.metrics[unique_id]
@@ -325,7 +320,7 @@ def _sort_values(dct):
def build_node_edges(nodes: List[ManifestNode]):
"""Build the forward and backward edges on the given list of ParsedNodes
"""Build the forward and backward edges on the given list of ManifestNodes
and return them as two separate dictionaries, each mapping unique IDs to
lists of edges.
"""
@@ -343,10 +338,10 @@ def build_node_edges(nodes: List[ManifestNode]):
# Build a map of children of macros and generic tests
def build_macro_edges(nodes: List[Any]):
forward_edges: Dict[str, List[str]] = {
n.unique_id: [] for n in nodes if n.unique_id.startswith("macro") or n.depends_on.macros
n.unique_id: [] for n in nodes if n.unique_id.startswith("macro") or n.depends_on_macros
}
for node in nodes:
for unique_id in node.depends_on.macros:
for unique_id in node.depends_on_macros:
if unique_id in forward_edges.keys():
forward_edges[unique_id].append(node.unique_id)
return _sort_values(forward_edges)
@@ -365,7 +360,7 @@ class Locality(enum.IntEnum):
@dataclass
class MacroCandidate:
locality: Locality
macro: ParsedMacro
macro: Macro
def __eq__(self, other: object) -> bool:
if not isinstance(other, MacroCandidate):
@@ -403,12 +398,7 @@ class MaterializationCandidate(MacroCandidate):
return NotImplemented
equal = self.specificity == other.specificity and self.locality == other.locality
if equal:
raise_compiler_error(
"Found two materializations with the name {} (packages {} and "
"{}). dbt cannot resolve this ambiguity".format(
self.macro.name, self.macro.package_name, other.macro.package_name
)
)
raise DuplicateMaterializationNameError(self.macro, other)
return equal
@@ -430,16 +420,14 @@ M = TypeVar("M", bound=MacroCandidate)
class CandidateList(List[M]):
def last(self) -> Optional[ParsedMacro]:
def last(self) -> Optional[Macro]:
if not self:
return None
self.sort()
return self[-1].macro
def _get_locality(
macro: ParsedMacro, root_project_name: str, internal_packages: Set[str]
) -> Locality:
def _get_locality(macro: Macro, root_project_name: str, internal_packages: Set[str]) -> Locality:
if macro.package_name == root_project_name:
return Locality.Root
elif macro.package_name in internal_packages:
@@ -465,16 +453,16 @@ class Disabled(Generic[D]):
target: D
MaybeMetricNode = Optional[Union[ParsedMetric, Disabled[ParsedMetric]]]
MaybeMetricNode = Optional[Union[Metric, Disabled[Metric]]]
MaybeDocumentation = Optional[ParsedDocumentation]
MaybeDocumentation = Optional[Documentation]
MaybeParsedSource = Optional[
Union[
ParsedSourceDefinition,
Disabled[ParsedSourceDefinition],
SourceDefinition,
Disabled[SourceDefinition],
]
]
@@ -492,13 +480,13 @@ def _update_into(dest: MutableMapping[str, T], new_item: T):
"""
unique_id = new_item.unique_id
if unique_id not in dest:
raise dbt.exceptions.RuntimeException(
raise dbt.exceptions.DbtRuntimeError(
f"got an update_{new_item.resource_type} call with an "
f"unrecognized {new_item.resource_type}: {new_item.unique_id}"
)
existing = dest[unique_id]
if new_item.original_file_path != existing.original_file_path:
raise dbt.exceptions.RuntimeException(
raise dbt.exceptions.DbtRuntimeError(
f"cannot update a {new_item.resource_type} to have a new file path!"
)
dest[unique_id] = new_item
@@ -514,7 +502,7 @@ class MacroMethods:
def find_macro_by_name(
self, name: str, root_project_name: str, package: Optional[str]
) -> Optional[ParsedMacro]:
) -> Optional[Macro]:
"""Find a macro in the graph by its name and package name, or None for
any package. The root project name is used to determine priority:
- locally defined macros come first
@@ -537,7 +525,7 @@ class MacroMethods:
def find_generate_macro_by_name(
self, component: str, root_project_name: str
) -> Optional[ParsedMacro]:
) -> Optional[Macro]:
"""
The `generate_X_name` macros are similar to regular ones, but ignore
imported packages.
@@ -606,11 +594,11 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
# is added it must all be added in the __reduce_ex__ method in the
# args tuple in the right position.
nodes: MutableMapping[str, ManifestNode] = field(default_factory=dict)
sources: MutableMapping[str, ParsedSourceDefinition] = field(default_factory=dict)
macros: MutableMapping[str, ParsedMacro] = field(default_factory=dict)
docs: MutableMapping[str, ParsedDocumentation] = field(default_factory=dict)
exposures: MutableMapping[str, ParsedExposure] = field(default_factory=dict)
metrics: MutableMapping[str, ParsedMetric] = field(default_factory=dict)
sources: MutableMapping[str, SourceDefinition] = field(default_factory=dict)
macros: MutableMapping[str, Macro] = field(default_factory=dict)
docs: MutableMapping[str, Documentation] = field(default_factory=dict)
exposures: MutableMapping[str, Exposure] = field(default_factory=dict)
metrics: MutableMapping[str, Metric] = field(default_factory=dict)
selectors: MutableMapping[str, Any] = field(default_factory=dict)
files: MutableMapping[str, AnySourceFile] = field(default_factory=dict)
metadata: ManifestMetadata = field(default_factory=ManifestMetadata)
@@ -658,7 +646,7 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
obj._lock = flags.MP_CONTEXT.Lock()
return obj
def sync_update_node(self, new_node: NonSourceCompiledNode) -> NonSourceCompiledNode:
def sync_update_node(self, new_node: ManifestNode) -> ManifestNode:
"""update the node with a lock. The only time we should want to lock is
when compiling an ephemeral ancestor of a node at runtime, because
multiple threads could be just-in-time compiling the same ephemeral
@@ -671,21 +659,21 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
with self._lock:
existing = self.nodes[new_node.unique_id]
if getattr(existing, "compiled", False):
# already compiled -> must be a NonSourceCompiledNode
return cast(NonSourceCompiledNode, existing)
# already compiled
return existing
_update_into(self.nodes, new_node)
return new_node
def update_exposure(self, new_exposure: ParsedExposure):
def update_exposure(self, new_exposure: Exposure):
_update_into(self.exposures, new_exposure)
def update_metric(self, new_metric: ParsedMetric):
def update_metric(self, new_metric: Metric):
_update_into(self.metrics, new_metric)
def update_node(self, new_node: ManifestNode):
_update_into(self.nodes, new_node)
def update_source(self, new_source: ParsedSourceDefinition):
def update_source(self, new_source: SourceDefinition):
_update_into(self.sources, new_source)
def build_flat_graph(self):
@@ -738,7 +726,7 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
def find_materialization_macro_by_name(
self, project_name: str, materialization_name: str, adapter_type: str
) -> Optional[ParsedMacro]:
) -> Optional[Macro]:
candidates: CandidateList = CandidateList(
chain.from_iterable(
self._materialization_candidates_for(
@@ -851,7 +839,7 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
return self.metrics[unique_id]
else:
# something terrible has happened
raise dbt.exceptions.InternalException(
raise dbt.exceptions.DbtInternalError(
"Expected node {} not found in manifest".format(unique_id)
)
@@ -943,8 +931,8 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
search_name = f"{target_source_name}.{target_table_name}"
candidates = _search_packages(current_project, node_package)
source: Optional[ParsedSourceDefinition] = None
disabled: Optional[List[ParsedSourceDefinition]] = None
source: Optional[SourceDefinition] = None
disabled: Optional[List[SourceDefinition]] = None
for pkg in candidates:
source = self.source_lookup.find(search_name, pkg, self)
@@ -968,8 +956,8 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
node_package: str,
) -> MaybeMetricNode:
metric: Optional[ParsedMetric] = None
disabled: Optional[List[ParsedMetric]] = None
metric: Optional[Metric] = None
disabled: Optional[List[Metric]] = None
candidates = _search_packages(current_project, node_package, target_metric_package)
for pkg in candidates:
@@ -992,7 +980,7 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
package: Optional[str],
current_project: str,
node_package: str,
) -> Optional[ParsedDocumentation]:
) -> Optional[Documentation]:
"""Resolve the given documentation. This follows the same algorithm as
resolve_ref except the is_enabled checks are unnecessary as docs are
always enabled.
@@ -1044,29 +1032,10 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
# Methods that were formerly in ParseResult
def add_macro(self, source_file: SourceFile, macro: ParsedMacro):
def add_macro(self, source_file: SourceFile, macro: Macro):
if macro.unique_id in self.macros:
# detect that the macro exists and emit an error
other_path = self.macros[macro.unique_id].original_file_path
# subtract 2 for the "Compilation Error" indent
# note that the line wrap eats newlines, so if you want newlines,
# this is the result :(
msg = line_wrap_message(
f"""\
dbt found two macros named "{macro.name}" in the project
"{macro.package_name}".
To fix this error, rename or remove one of the following
macros:
- {macro.original_file_path}
- {other_path}
""",
subtract=2,
)
raise_compiler_error(msg)
raise DuplicateMacroInPackageError(macro=macro, macro_mapping=self.macros)
self.macros[macro.unique_id] = macro
source_file.macros.append(macro.unique_id)
@@ -1086,30 +1055,30 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
self.sources[source.unique_id] = source # type: ignore
source_file.sources.append(source.unique_id)
def add_node_nofile(self, node: ManifestNodes):
def add_node_nofile(self, node: ManifestNode):
# nodes can't be overwritten!
_check_duplicates(node, self.nodes)
self.nodes[node.unique_id] = node
def add_node(self, source_file: AnySourceFile, node: ManifestNodes, test_from=None):
def add_node(self, source_file: AnySourceFile, node: ManifestNode, test_from=None):
self.add_node_nofile(node)
if isinstance(source_file, SchemaSourceFile):
if isinstance(node, ParsedGenericTestNode):
if isinstance(node, GenericTestNode):
assert test_from
source_file.add_test(node.unique_id, test_from)
if isinstance(node, ParsedMetric):
if isinstance(node, Metric):
source_file.metrics.append(node.unique_id)
if isinstance(node, ParsedExposure):
if isinstance(node, Exposure):
source_file.exposures.append(node.unique_id)
else:
source_file.nodes.append(node.unique_id)
def add_exposure(self, source_file: SchemaSourceFile, exposure: ParsedExposure):
def add_exposure(self, source_file: SchemaSourceFile, exposure: Exposure):
_check_duplicates(exposure, self.exposures)
self.exposures[exposure.unique_id] = exposure
source_file.exposures.append(exposure.unique_id)
def add_metric(self, source_file: SchemaSourceFile, metric: ParsedMetric):
def add_metric(self, source_file: SchemaSourceFile, metric: Metric):
_check_duplicates(metric, self.metrics)
self.metrics[metric.unique_id] = metric
source_file.metrics.append(metric.unique_id)
@@ -1121,20 +1090,20 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
else:
self.disabled[node.unique_id] = [node]
def add_disabled(self, source_file: AnySourceFile, node: CompileResultNode, test_from=None):
def add_disabled(self, source_file: AnySourceFile, node: ResultNode, test_from=None):
self.add_disabled_nofile(node)
if isinstance(source_file, SchemaSourceFile):
if isinstance(node, ParsedGenericTestNode):
if isinstance(node, GenericTestNode):
assert test_from
source_file.add_test(node.unique_id, test_from)
if isinstance(node, ParsedMetric):
if isinstance(node, Metric):
source_file.metrics.append(node.unique_id)
if isinstance(node, ParsedExposure):
if isinstance(node, Exposure):
source_file.exposures.append(node.unique_id)
else:
source_file.nodes.append(node.unique_id)
def add_doc(self, source_file: SourceFile, doc: ParsedDocumentation):
def add_doc(self, source_file: SourceFile, doc: Documentation):
_check_duplicates(doc, self.docs)
self.docs[doc.unique_id] = doc
source_file.docs.append(doc.unique_id)
@@ -1192,27 +1161,27 @@ class WritableManifest(ArtifactMixin):
nodes: Mapping[UniqueID, ManifestNode] = field(
metadata=dict(description=("The nodes defined in the dbt project and its dependencies"))
)
sources: Mapping[UniqueID, ParsedSourceDefinition] = field(
sources: Mapping[UniqueID, SourceDefinition] = field(
metadata=dict(description=("The sources defined in the dbt project and its dependencies"))
)
macros: Mapping[UniqueID, ParsedMacro] = field(
macros: Mapping[UniqueID, Macro] = field(
metadata=dict(description=("The macros defined in the dbt project and its dependencies"))
)
docs: Mapping[UniqueID, ParsedDocumentation] = field(
docs: Mapping[UniqueID, Documentation] = field(
metadata=dict(description=("The docs defined in the dbt project and its dependencies"))
)
exposures: Mapping[UniqueID, ParsedExposure] = field(
exposures: Mapping[UniqueID, Exposure] = field(
metadata=dict(
description=("The exposures defined in the dbt project and its dependencies")
)
)
metrics: Mapping[UniqueID, ParsedMetric] = field(
metrics: Mapping[UniqueID, Metric] = field(
metadata=dict(description=("The metrics defined in the dbt project and its dependencies"))
)
selectors: Mapping[UniqueID, Any] = field(
metadata=dict(description=("The selectors defined in selectors.yml"))
)
disabled: Optional[Mapping[UniqueID, List[CompileResultNode]]] = field(
disabled: Optional[Mapping[UniqueID, List[GraphMemberNode]]] = field(
metadata=dict(description="A mapping of the disabled nodes in the target")
)
parent_map: Optional[NodeEdgeMap] = field(
@@ -1242,9 +1211,9 @@ class WritableManifest(ArtifactMixin):
return dct
def _check_duplicates(value: HasUniqueID, src: Mapping[str, HasUniqueID]):
def _check_duplicates(value: BaseNode, src: Mapping[str, BaseNode]):
if value.unique_id in src:
raise_duplicate_resource_name(value, src[value.unique_id])
raise DuplicateResourceNameError(value, src[value.unique_id])
K_T = TypeVar("K_T")
@@ -1253,7 +1222,7 @@ V_T = TypeVar("V_T")
def _expect_value(key: K_T, src: Mapping[K_T, V_T], old_file: SourceFile, name: str) -> V_T:
if key not in src:
raise CompilationException(
raise CompilationError(
'Expected to find "{}" in cached "result.{}" based '
"on cached file information: {}!".format(key, name, old_file)
)

View File

@@ -12,7 +12,7 @@ class MetricReference(object):
class ResolvedMetricReference(MetricReference):
"""
Simple proxy over a ParsedMetric which delegates property
Simple proxy over a Metric which delegates property
lookups to the underlying node. Also adds helper functions
for working with metrics (ie. __str__ and templating functions)
"""

View File

@@ -9,7 +9,7 @@ from dbt.dataclass_schema import (
)
from dbt.contracts.graph.unparsed import AdditionalPropertiesAllowed, Docs
from dbt.contracts.graph.utils import validate_color
from dbt.exceptions import InternalException, CompilationException
from dbt.exceptions import DbtInternalError, CompilationError
from dbt.contracts.util import Replaceable, list_str
from dbt import hooks
from dbt.node_types import NodeType
@@ -30,7 +30,7 @@ def _get_meta_value(cls: Type[M], fld: Field, key: str, default: Any) -> M:
try:
return cls(value)
except ValueError as exc:
raise InternalException(f"Invalid {cls} value: {value}") from exc
raise DbtInternalError(f"Invalid {cls} value: {value}") from exc
def _set_meta_value(obj: M, key: str, existing: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
@@ -140,17 +140,17 @@ def _merge_field_value(
return _listify(self_value) + _listify(other_value)
elif merge_behavior == MergeBehavior.Update:
if not isinstance(self_value, dict):
raise InternalException(f"expected dict, got {self_value}")
raise DbtInternalError(f"expected dict, got {self_value}")
if not isinstance(other_value, dict):
raise InternalException(f"expected dict, got {other_value}")
raise DbtInternalError(f"expected dict, got {other_value}")
value = self_value.copy()
value.update(other_value)
return value
elif merge_behavior == MergeBehavior.DictKeyAppend:
if not isinstance(self_value, dict):
raise InternalException(f"expected dict, got {self_value}")
raise DbtInternalError(f"expected dict, got {self_value}")
if not isinstance(other_value, dict):
raise InternalException(f"expected dict, got {other_value}")
raise DbtInternalError(f"expected dict, got {other_value}")
new_dict = {}
for key in self_value.keys():
new_dict[key] = _listify(self_value[key])
@@ -172,7 +172,7 @@ def _merge_field_value(
return new_dict
else:
raise InternalException(f"Got an invalid merge_behavior: {merge_behavior}")
raise DbtInternalError(f"Got an invalid merge_behavior: {merge_behavior}")
def insensitive_patterns(*patterns: str):
@@ -227,7 +227,7 @@ class BaseConfig(AdditionalPropertiesAllowed, Replaceable):
msg = (
'Error, tried to delete config key "{}": Cannot delete ' "built-in keys"
).format(key)
raise CompilationException(msg)
raise CompilationError(msg)
else:
del self._extra[key]

View File

@@ -11,7 +11,7 @@ from dbt.contracts.util import (
# trigger the PathEncoder
import dbt.helper_types # noqa:F401
from dbt.exceptions import CompilationException, ParsingException
from dbt.exceptions import CompilationError, ParsingError
from dbt.dataclass_schema import dbtClassMixin, StrEnum, ExtensibleDbtClassMixin, ValidationError
@@ -222,7 +222,7 @@ class ExternalPartition(AdditionalPropertiesAllowed, Replaceable):
def __post_init__(self):
if self.name == "" or self.data_type == "":
raise CompilationException("External partition columns must have names and data types")
raise CompilationError("External partition columns must have names and data types")
@dataclass
@@ -484,9 +484,9 @@ class UnparsedMetric(dbtClassMixin, Replaceable):
name: str
label: str
calculation_method: str
timestamp: str
expression: str
description: str = ""
timestamp: Optional[str] = None
time_grains: List[str] = field(default_factory=list)
dimensions: List[str] = field(default_factory=list)
window: Optional[MetricTime] = None
@@ -514,10 +514,20 @@ class UnparsedMetric(dbtClassMixin, Replaceable):
errors.append("must contain only letters, numbers and underscores")
if errors:
raise ParsingException(
raise ParsingError(
f"The metric name '{data['name']}' is invalid. It {', '.join(e for e in errors)}"
)
if data.get("timestamp") is None and data.get("time_grains") is not None:
raise ValidationError(
f"The metric '{data['name']} has time_grains defined but is missing a timestamp dimension."
)
if data.get("timestamp") is None and data.get("window") is not None:
raise ValidationError(
f"The metric '{data['name']} has a window defined but is missing a timestamp dimension."
)
if data.get("model") is None and data.get("calculation_method") != "derived":
raise ValidationError("Non-derived metrics require a 'model' property")

View File

@@ -55,6 +55,12 @@ class LocalPackage(Package):
RawVersion = Union[str, float]
@dataclass
class TarballPackage(Package):
tarball: str
name: str
@dataclass
class GitPackage(Package):
git: str
@@ -82,7 +88,7 @@ class RegistryPackage(Package):
return [str(self.version)]
PackageSpec = Union[LocalPackage, GitPackage, RegistryPackage]
PackageSpec = Union[LocalPackage, TarballPackage, GitPackage, RegistryPackage]
@dataclass
@@ -216,7 +222,7 @@ class Project(HyphenatedDbtClassMixin, Replaceable):
),
)
packages: List[PackageSpec] = field(default_factory=list)
query_comment: Optional[Union[QueryComment, NoValue, str]] = NoValue()
query_comment: Optional[Union[QueryComment, NoValue, str]] = field(default_factory=NoValue)
@classmethod
def validate(cls, data):
@@ -243,6 +249,7 @@ class UserConfig(ExtensibleDbtClassMixin, Replaceable, UserConfigContract):
printer_width: Optional[int] = None
write_json: Optional[bool] = None
warn_error: Optional[bool] = None
warn_error_options: Optional[Dict[str, Union[str, List[str]]]] = None
log_format: Optional[str] = None
debug: Optional[bool] = None
version_check: Optional[bool] = None
@@ -251,7 +258,6 @@ class UserConfig(ExtensibleDbtClassMixin, Replaceable, UserConfigContract):
static_parser: Optional[bool] = None
indirect_selection: Optional[str] = None
cache_selected_only: Optional[bool] = None
event_buffer_size: Optional[int] = None
@dataclass

View File

@@ -9,7 +9,7 @@ from typing_extensions import Protocol
from dbt.dataclass_schema import dbtClassMixin, StrEnum
from dbt.contracts.util import Replaceable
from dbt.exceptions import raise_dataclass_not_dict, CompilationException
from dbt.exceptions import CompilationError, DataclassNotDictError
from dbt.utils import deep_merge
@@ -43,10 +43,10 @@ class FakeAPIObject(dbtClassMixin, Replaceable, Mapping):
raise KeyError(key) from None
def __iter__(self):
raise_dataclass_not_dict(self)
raise DataclassNotDictError(self)
def __len__(self):
raise_dataclass_not_dict(self)
raise DataclassNotDictError(self)
def incorporate(self, **kwargs):
value = self.to_dict(omit_none=True)
@@ -88,13 +88,11 @@ class Path(FakeAPIObject):
def __post_init__(self):
# handle pesky jinja2.Undefined sneaking in here and messing up rende
if not isinstance(self.database, (type(None), str)):
raise CompilationException("Got an invalid path database: {}".format(self.database))
raise CompilationError("Got an invalid path database: {}".format(self.database))
if not isinstance(self.schema, (type(None), str)):
raise CompilationException("Got an invalid path schema: {}".format(self.schema))
raise CompilationError("Got an invalid path schema: {}".format(self.schema))
if not isinstance(self.identifier, (type(None), str)):
raise CompilationException(
"Got an invalid path identifier: {}".format(self.identifier)
)
raise CompilationError("Got an invalid path identifier: {}".format(self.identifier))
def get_lowered_part(self, key: ComponentName) -> Optional[str]:
part = self.get_part(key)

Some files were not shown because too many files have changed in this diff Show More