Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use fastjsonschema instead of jsonschema for validation #10249

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/Under the Hood-20240601-140218.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Under the Hood
body: Replace jsonschema validation with fastjsonschema
time: 2024-06-01T14:02:18.612839-04:00
custom:
Author: gshank
Issue: "10248"
2 changes: 2 additions & 0 deletions core/dbt/config/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,9 @@ def _credentials_from_profile(
typename = profile.pop("type")
try:
cls = load_plugin(typename)
print(f"--- in _credentials_from_profile. cls: {cls}")
data = cls.translate_aliases(profile)
print(f"--- --- data: {data}")
cls.validate(data)
credentials = cls.from_dict(data)
except (DbtRuntimeError, ValidationError) as e:
Expand Down
2 changes: 1 addition & 1 deletion core/dbt/config/selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def selectors_from_dict(cls, data: Dict[str, Any]) -> "SelectorConfig":
validate_selector_default(selector_file)
selectors = parse_from_selectors_definition(selector_file)
except ValidationError as exc:
yaml_sel_cfg = yaml.dump(exc.instance)
yaml_sel_cfg = yaml.dump(exc.value)
raise DbtSelectorsError(
f"Could not parse selector file data: \n{yaml_sel_cfg}\n"
f"Valid root-level selector definitions: "
Expand Down
6 changes: 4 additions & 2 deletions core/dbt/parser/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,12 +457,14 @@ def parse(self) -> ParseResult:
versioned_test_blocks: List[VersionedTestBlock] = []

# get list of 'node' objects
# UnparsedNodeUpdate (TestablePatchParser, models, seeds, snapshots)
# UnparsedNodeUpdate (TestablePatchParser, seeds, snapshots)
# = HasColumnTests, HasTests
# UnparsedAnalysisUpdate (UnparsedAnalysisParser, analyses)
# = HasColumnDocs, HasDocs
# UnparsedMacroUpdate (MacroPatchParser, 'macros')
# = HasDocs
# UnparsedModelUpdate (ModelPatchParser, models)
# = HasColumnTests, HasTests
# correspond to this parser's 'key'
for node in self.get_unparsed_target():
# node_block is a TargetBlock (Macro or Analysis)
Expand Down Expand Up @@ -562,7 +564,7 @@ def validate_data_tests(self, data) -> None:
# Raise a validation error if the user has defined both names
def validate_and_rename(data, is_root_project: bool) -> None:
if data.get("tests"):
if "tests" in data and "data_tests" in data:
if data.get("data_tests"):
raise ValidationError(
"Invalid test config: cannot have both 'tests' and 'data_tests' defined"
)
Expand Down
2 changes: 1 addition & 1 deletion dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
git+https://github.com/dbt-labs/dbt-adapters.git@main
git+https://github.com/dbt-labs/dbt-adapters.git@main#subdirectory=dbt-tests-adapter
git+https://github.com/dbt-labs/dbt-common.git@main
git+https://github.com/dbt-labs/dbt-common.git@try_fastjsonschema
git+https://github.com/dbt-labs/dbt-postgres.git@main
# black must match what's in .pre-commit-config.yaml to be sure local env matches CI
black==24.3.0
Expand Down
8 changes: 2 additions & 6 deletions tests/functional/basic/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,7 @@ def test_invalid_version(self, project):
update_config_file({"version": "invalid"}, "dbt_project.yml")
with pytest.raises(ProjectContractError) as excinfo:
run_dbt()
assert "at path ['version']: 'invalid' is not valid under any of the given schemas" in str(
excinfo.value
)
assert "Invalid value 'invalid'" in str(excinfo.value)


class TestProjectDbtCloudConfig:
Expand Down Expand Up @@ -113,9 +111,7 @@ def test_dbt_cloud_invalid(self, project):
run_dbt()
config = {"name": "test", "profile": "test", "dbt-cloud": "Some string"}
update_config_file(config, "dbt_project.yml")
expected_err = (
"at path ['dbt-cloud']: 'Some string' is not valid under any of the given schemas"
)
expected_err = "Invalid value 'Some string'"
with pytest.raises(ProjectContractError) as excinfo:
run_dbt()
assert expected_err in str(excinfo.value)
Expand Down
7 changes: 3 additions & 4 deletions tests/functional/configs/test_disabled_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,9 +394,8 @@ def models(self):
"my_model.sql": my_model,
}

def test_invalis_config(self, project):
def test_invalid_config(self, project):
with pytest.raises(ValidationError) as exc:
run_dbt(["parse"])
exc_str = " ".join(str(exc.value).split()) # flatten all whitespace
expected_msg = "'True and False' is not of type 'boolean'"
assert expected_msg in exc_str
expected_msg = "Invalid value 'True and False': data.enabled must be boolean"
assert expected_msg in exc.value.msg
2 changes: 1 addition & 1 deletion tests/functional/exposures/test_exposure_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,5 @@ def models(self):
def test_exposure_config_yaml_level(self, project):
with pytest.raises(ValidationError) as excinfo:
run_dbt(["parse"])
expected_msg = "'True and False' is not of type 'boolean'"
expected_msg = "Invalid value 'True and False': data.enabled must be boolean"
assert expected_msg in str(excinfo.value)
2 changes: 1 addition & 1 deletion tests/functional/metrics/test_metric_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def models(self):
def test_invalid_config_metric(self, project):
with pytest.raises(ValidationError) as excinfo:
run_dbt(["parse"])
expected_msg = "'True and False' is not of type 'boolean'"
expected_msg = "Invalid value 'True and False': data.enabled must be boolean"
assert expected_msg in str(excinfo.value)


Expand Down
10 changes: 7 additions & 3 deletions tests/functional/postgres/test_postgres_indexes.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,11 @@ def models(self):
def test_invalid_index_configs(self, project):
results, output = run_dbt_and_capture(expect_pass=False)
assert len(results) == 4
assert re.search(r"columns.*is not of type 'array'", output)
assert re.search(r"unique.*is not of type 'boolean'", output)
assert re.search(r"'columns' is a required property", output)
# Could not parse index config: Invalid value 'column_a, column_b': data.columns must be array
assert re.search(r"columns must be array", output)
# Could not parse index config: Invalid value 'yes': data.unique must be boolean
assert re.search(r"unique must be boolean", output)
# Could not parse index config: Invalid value '{'unique': True}': data must contain ['columns'] properties
assert re.search(r"data must contain \['columns'\] properties", output)
# Database Error in model invalid_type (models/invalid_type.sql) / access method "non_existent_type" does not exist
assert re.search(r"Database Error in model invalid_type", output)
2 changes: 1 addition & 1 deletion tests/functional/sources/test_source_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,5 +177,5 @@ def models(self):
def test_invalid_config_source(self, project):
with pytest.raises(ValidationError) as excinfo:
run_dbt(["parse"])
expected_msg = "'True and False' is not of type 'boolean'"
expected_msg = "Invalid value 'True and False': data.enabled must be boolean"
assert expected_msg in str(excinfo.value)
Loading