Replace voluptuous with validx for schema validation

This commit is contained in:
Andrew Halberstadt 2021-04-15 13:42:20 -04:00
Родитель 66a25fe39a
Коммит ceab0279e7
6 изменённых файлов: 128 добавлений и 114 удалений

Просмотреть файл

@ -2,4 +2,4 @@
multi_line_output=3
include_trailing_comma=True
line_length=88
known_third_party = adr,appdirs,boto3,botocore,cachy,loguru,lru,pytest,requests,responses,taskcluster,taskcluster_urls,tomlkit,voluptuous,yaml,zstandard
known_third_party = adr,appdirs,boto3,botocore,cachy,loguru,lru,pytest,requests,responses,taskcluster,taskcluster_urls,tomlkit,validx,yaml,zstandard

Просмотреть файл

@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
from abc import ABC, abstractproperty
from pprint import pprint
from typing import Any, Dict, List, Tuple, Union
import voluptuous
import validx
from loguru import logger
from mozci.data.contract import all_contracts
@ -82,21 +81,8 @@ class DataHandler:
# Validate output.
try:
contract.validate_out(result)
except voluptuous.MultipleInvalid as e:
except validx.exc.SchemaError:
print(f"Result from contract '{name}' does not conform to schema!")
for error in e.errors:
if not error.path:
continue
problem = result
for i in error.path[:-1]:
problem = problem[i]
print(
f'\nProblem with item "{error.path[-1]}" in the following object:'
)
pprint(problem, indent=2, depth=2)
print()
raise
return result

Просмотреть файл

@ -2,9 +2,9 @@
from dataclasses import dataclass
from textwrap import dedent
from typing import Tuple
from typing import Tuple, Union
from voluptuous import All, Any, Length, Marker, Optional, Required, Schema
import validx as v
from mozci.task import TestTask
@ -13,46 +13,42 @@ from mozci.task import TestTask
class Contract:
name: str
description: str
validate_in: Schema
validate_out: Schema
validate_in: v.Dict
validate_out: Union[v.Dict, v.List]
_contracts: Tuple[Contract, ...] = (
Contract(
name="push_tasks",
description="Data about the tasks that ran on a given push.",
validate_in=Schema(
validate_in=v.Dict(
{
Required("branch"): str,
Required("rev"): str,
"branch": v.Str(),
"rev": v.Str(),
}
),
validate_out=Schema(
[
validate_out=v.List(
v.Dict(
{
Required("id"): str,
Required("label"): str,
Required("state"): Any(
"completed",
"running",
"pending",
"unscheduled",
"id": v.Str(),
"label": v.Str(),
"state": v.Str(
options=["completed", "running", "pending", "unscheduled"]
),
Required("tags"): {
Marker(str, description="tag name"): Marker(
str, description="tag value"
)
},
Optional("duration"): int,
Optional("result"): Any(
"passed",
"failed",
"exception",
"canceled",
"superseded",
"tags": v.Dict(extra=(v.Str(), v.Str())),
"duration": v.Int(),
"result": v.Str(
options=[
"passed",
"failed",
"exception",
"canceled",
"superseded",
]
),
}
]
},
optional=["duration", "result"],
)
),
),
Contract(
@ -63,75 +59,73 @@ _contracts: Tuple[Contract, ...] = (
without a classification need not be present.
"""
),
validate_in=Schema(
validate_in=v.Dict(
{
Required("branch"): str,
Required("rev"): str,
"branch": v.Str(),
"rev": v.Str(),
}
),
validate_out=Schema(
{
Marker(str, description="task id"): {
Required("classification"): Any(
"autoclassified intermittent",
"infra",
"intermittent",
"expected fail",
"fixed by commit",
"not classified",
),
Optional("classification_note"): str,
}
}
validate_out=v.Dict(
extra=(
v.Str(),
v.Dict(
{
"classification": v.Str(
options=[
"autoclassified intermittent",
"infra",
"intermittent",
"expected fail",
"fixed by commit",
"not classified",
],
),
"classification_note": v.Str(),
},
optional=["classification_note"],
),
)
),
),
Contract(
name="push_revisions",
description="Data from the VCS about a given push.",
validate_in=Schema(
validate_in=v.Dict(
{
Required("from_date"): str,
Required("to_date"): str,
Required("branch"): str,
"from_date": v.Str(),
"to_date": v.Str(),
"branch": v.Str(),
}
),
validate_out=Schema(
[
validate_out=v.List(
v.Dict(
{
Required("pushid"): int,
Required("date"): int,
Required("revs"): [str],
"pushid": v.Int(),
"date": v.Int(),
"revs": v.List(v.Str()),
}
]
)
),
),
Contract(
name="test_task_groups",
description="A dict of test groups and their results for a given TestTask.",
validate_in=Schema(
validate_in=v.Dict(
{
Required("task"): TestTask,
}
),
validate_out=Schema(
{
Marker(All(str, Length(min=1)), description="group name"): Marker(
bool, description="group result"
)
"task": v.Type(TestTask),
}
),
validate_out=v.Dict(extra=(v.Str(minlen=1), v.Bool())),
),
Contract(
name="test_task_errors",
description="A list of errors for a given TestTask.",
validate_in=Schema(
validate_in=v.Dict(
{
Required("task"): TestTask,
"task": v.Type(TestTask),
}
),
validate_out=Schema(
[Marker(All(str, Length(min=1)), description="error message")]
),
validate_out=v.List(v.Str(minlen=1)),
),
)

60
poetry.lock сгенерированный
Просмотреть файл

@ -815,6 +815,14 @@ secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "cer
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
brotli = ["brotlipy (>=0.6.0)"]
[[package]]
name = "validx"
version = "0.7"
description = "fast, powerful, and flexible validator with sane syntax"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "virtualenv"
version = "20.4.3"
@ -834,14 +842,6 @@ six = ">=1.9.0,<2"
docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"]
testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)", "xonsh (>=0.9.16)"]
[[package]]
name = "voluptuous"
version = "0.12.1"
description = ""
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "win32-setctime"
version = "1.0.3"
@ -898,7 +898,7 @@ adr = ["adr"]
[metadata]
lock-version = "1.1"
python-versions = ">=3.7,<4"
content-hash = "c60eb461e650c7814c63fdd58592748f9b0b60862966715f0dc142f1a61b944a"
content-hash = "71ebe5bb04d13b4d18fc0cd6fa9df66400ed29a6cb5cc4ae1fd789b2a71a9788"
[metadata.files]
adr = [
@ -1351,14 +1351,48 @@ urllib3 = [
{file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"},
{file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"},
]
validx = [
{file = "ValidX-0.7-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:320984109c790d8a2568804290419cba1642307ffc9ef26f692928df7f313c47"},
{file = "ValidX-0.7-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:402bcfa2945fb723a066c0fca7c63765880b2732c756107ec4984490d20e3203"},
{file = "ValidX-0.7-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:1ada2c7240ce85e334b86bd22f343faff13742f7eadf06f2c232c76aa9d56c48"},
{file = "ValidX-0.7-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:8a6376a43ff4d71d450dc318f11caf7aadbd77e06a4a1fe6cdb7c8d540023fe7"},
{file = "ValidX-0.7-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:5ef04d560706d4ea01a803c61f90b597b6703bafa45adda88b823944756cb487"},
{file = "ValidX-0.7-cp35-cp35m-win32.whl", hash = "sha256:719587896f3a31a8774ab695433e40bfe56f2aabecdd57f738015a40ebea78a0"},
{file = "ValidX-0.7-cp35-cp35m-win_amd64.whl", hash = "sha256:66c20bd73f0a965de86f3142b08c8231ae3cd781abf7553d20fdb7276aa1dde9"},
{file = "ValidX-0.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a4faf64c701018fe2d7022b3fca3498ce2870c98ea2ce0844c6137c40196786"},
{file = "ValidX-0.7-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:db1bdf1485b64da7b9581799ee6dfc3397b14457c1c744072640ad4405fcb6ee"},
{file = "ValidX-0.7-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:b06a33a2ebdaae7cfcd7294cdf150cf29c1a500838056387d68d84473192d5be"},
{file = "ValidX-0.7-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:15e186badfa120c22c828c594c346ff8fd34876af628f7cddf6363d6a76b6f95"},
{file = "ValidX-0.7-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e9b56e49491c9b8a2da578ebbb411aeb7bcae05cf5eda632591921738c69702f"},
{file = "ValidX-0.7-cp36-cp36m-win32.whl", hash = "sha256:e0d944ccbb94246419faf3d12b93b8c2ec953152ec0ad01e71d8201da5230e73"},
{file = "ValidX-0.7-cp36-cp36m-win_amd64.whl", hash = "sha256:9d3ddf6574940f7115534be7b6ce21e114a1f1fff46dff369cec779abe498d1c"},
{file = "ValidX-0.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38322f822f941b1b5d810836c6dcf13875c4d13fb5e9d0718e6bcfce351033b9"},
{file = "ValidX-0.7-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ab441d1621d8e26237537710d2b020a7bbe70b573c0139ce5bfc6950da15d391"},
{file = "ValidX-0.7-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fa277c24ce52eac619e82298de929759bb23b4dbdec28ba6e9f60e40198d2509"},
{file = "ValidX-0.7-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:152dcc598f95e2d7ed8cf6f82fb911f1fd4d2b7111a1a3a933a1116a8b44e830"},
{file = "ValidX-0.7-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:afe1d4f1606d787a5f9a020dff6f71267feadd87e9a4a8ca5472a2d7f1edf1ac"},
{file = "ValidX-0.7-cp37-cp37m-win32.whl", hash = "sha256:a36c3d271cdcfbeefcd921937ffa405e21025c54219687c01a5954b73d6a7e74"},
{file = "ValidX-0.7-cp37-cp37m-win_amd64.whl", hash = "sha256:906f096360156303625b6f425414d1a4b4e9b3b72919379a95a1a9f4cd63098b"},
{file = "ValidX-0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a9b888a9b6331f33b47071976d28fcee977c77aea2307da709417f1fb62e2202"},
{file = "ValidX-0.7-cp38-cp38-manylinux1_i686.whl", hash = "sha256:bc63d0406041e12182451d6064f51338692bb312fd15686bf9970a5b30a3e4c7"},
{file = "ValidX-0.7-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:95f900339a7016857813e22bd28c066e12a9dab49e0f98c22550d6dfde1dfdd1"},
{file = "ValidX-0.7-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:f3bea80f0f8d440ba233b22e3b7153f4f08d20dcf62f209659b5b295dcb39740"},
{file = "ValidX-0.7-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:dff33c40f90fed98ff73d50a309c3206a6e7da225e0fe0c97e9e13d5273a231d"},
{file = "ValidX-0.7-cp38-cp38-win32.whl", hash = "sha256:8313974e13d395f59341455f08dda5bd84bf56060e033dd918bfec1156310c5d"},
{file = "ValidX-0.7-cp38-cp38-win_amd64.whl", hash = "sha256:780bbf248223f67d5da51699c10ae348ea636706db2425ec50c20679a1f75462"},
{file = "ValidX-0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a82987dda3a24fc44eb849a3df0661ffa7d807aa071811e0d8fc36eed0fe58ed"},
{file = "ValidX-0.7-cp39-cp39-manylinux1_i686.whl", hash = "sha256:299b0d5eda95eb409a9771c23b1deb3c6f7499c995b6a52c3e760afd688fb3ec"},
{file = "ValidX-0.7-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b58c861953e0785de7d02c496d2252e10841ca8e12458b37341fa5c6ebaea00d"},
{file = "ValidX-0.7-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:1d655cbee8c5812bd92a5f83de66594cfc66bd3e0391c9f99bd11c25572d0013"},
{file = "ValidX-0.7-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:4e086ee056a0414bbe2f06267d9c9d6c5c1a9848043b40fdf9e858ec86e3f618"},
{file = "ValidX-0.7-cp39-cp39-win32.whl", hash = "sha256:227153f3ae19630a8ada0f6bee3d824976df8ec60e41fc56921eb6cfa9f37136"},
{file = "ValidX-0.7-cp39-cp39-win_amd64.whl", hash = "sha256:d7783ac9ca78ccf24cfeb3606ada82ea211a79a27d12a98878529adeff09f342"},
{file = "ValidX-0.7.tar.gz", hash = "sha256:ffbd835c3449a89c056c814205729532b9a179a4005bb99441562e2f1ab46cba"},
]
virtualenv = [
{file = "virtualenv-20.4.3-py2.py3-none-any.whl", hash = "sha256:83f95875d382c7abafe06bd2a4cdd1b363e1bb77e02f155ebe8ac082a916b37c"},
{file = "virtualenv-20.4.3.tar.gz", hash = "sha256:49ec4eb4c224c6f7dd81bb6d0a28a09ecae5894f4e593c89b0db0885f565a107"},
]
voluptuous = [
{file = "voluptuous-0.12.1-py3-none-any.whl", hash = "sha256:8ace33fcf9e6b1f59406bfaf6b8ec7bcc44266a9f29080b4deb4fe6ff2492386"},
{file = "voluptuous-0.12.1.tar.gz", hash = "sha256:663572419281ddfaf4b4197fd4942d181630120fb39b333e3adad70aeb56444b"},
]
win32-setctime = [
{file = "win32_setctime-1.0.3-py3-none-any.whl", hash = "sha256:dc925662de0a6eb987f0b01f599c01a8236cb8c62831c22d9cada09ad958243e"},
{file = "win32_setctime-1.0.3.tar.gz", hash = "sha256:4e88556c32fdf47f64165a2180ba4552f8bb32c1103a2fafd05723a0bd42bd4b"},

Просмотреть файл

@ -19,7 +19,6 @@ zstandard = {version = "~0", optional = true}
python3-memcached = {version = "~1", optional = true}
redis = {version = "~3", optional = true}
requests = "~2"
voluptuous = "~0"
flake8 = "~3"
pyyaml = "~5"
taskcluster = ">=38"
@ -27,6 +26,7 @@ lru-dict = "^1.1.7"
# Optional dependencies
adr = { version = "~0", optional = true }
ValidX = "^0.7"
[tool.poetry.dev-dependencies]
pre-commit = "^2.12"

Просмотреть файл

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
import pytest
from voluptuous import MultipleInvalid, Required, Schema
import validx as v
from mozci import data
from mozci.data.base import DataHandler, DataSource
@ -16,26 +16,26 @@ FAKE_CONTRACTS = (
Contract(
name="foo",
description="test",
validate_in=Schema({Required("label"): str}),
validate_out=Schema({Required("count"): int}),
validate_in=v.Dict({"label": v.Str()}),
validate_out=v.Dict({"count": v.Int()}),
),
Contract(
name="bar",
description="test",
validate_in=Schema({Required("desc"): str}),
validate_out=Schema({Required("amount"): int}),
validate_in=v.Dict({"desc": v.Str()}),
validate_out=v.Dict({"amount": v.Int()}),
),
Contract(
name="baz",
description="test",
validate_in=Schema({Required("id"): str}),
validate_out=Schema({Required("sum"): int}),
validate_in=v.Dict({"id": v.Str()}),
validate_out=v.Dict({"sum": v.Int()}),
),
Contract(
name="incomplete",
description="test",
validate_in=Schema({}),
validate_out=Schema({}),
validate_in=v.Dict({}),
validate_out=v.Dict({}),
),
)
@ -67,7 +67,7 @@ def test_data_handler(monkeypatch):
monkeypatch.setattr(DataHandler, "ALL_SOURCES", {"fake": FakeSource()})
handler = DataHandler("fake")
with pytest.raises(MultipleInvalid):
with pytest.raises(v.exc.SchemaError):
handler.get("baz")
with pytest.raises(SourcesNotFound):
@ -79,10 +79,10 @@ def test_data_handler(monkeypatch):
with pytest.raises(ContractNotFound):
handler.get("fleem")
with pytest.raises(MultipleInvalid):
with pytest.raises(v.exc.SchemaError):
handler.get("foo")
with pytest.raises(MultipleInvalid):
with pytest.raises(v.exc.SchemaError):
handler.get("foo", label="foo")
assert handler.get("bar", desc="tada") == {"amount": 1}