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

Add error wrappers for pydantic validations #103

Merged
merged 5 commits into from
Oct 18, 2023
Merged
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
9 changes: 8 additions & 1 deletion basemodels/manifest/data/helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import requests
from pydantic import ValidationError, BaseModel, HttpUrl
from pydantic.error_wrappers import ErrorWrapper

from basemodels.constants import SUPPORTED_CONTENT_TYPES


Expand All @@ -13,4 +15,9 @@ def validate_content_type(uri: str) -> None:
response.raise_for_status()
content_type = response.headers.get("Content-Type", "")
if content_type not in SUPPORTED_CONTENT_TYPES:
raise ValidationError(f"Unsupported type {content_type}", ExampleResourceModel())
raise ValidationError(
[
ErrorWrapper(ValueError(f"Unsupported type {content_type}"), "answer_example_uri")
],
ExampleResourceModel
)
16 changes: 13 additions & 3 deletions basemodels/manifest/data/requester_question_example.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from typing import Union

from pydantic.error_wrappers import ErrorWrapper
from requests import RequestException
from pydantic import ValidationError
from .helpers import validate_content_type, ExampleResourceModel
Expand All @@ -18,14 +20,22 @@ def validate_requester_example_image(
uri_val = uri
validate_content_type(uri)
else:
raise ValidationError(f"Not supported format for requester_question_example.")
raise ValueError(f"Not supported format for requester_question_example.")
except RequestException as e:
raise ValidationError(
f"could not retrieve requester example ({uri_val})",
[
ErrorWrapper(ValueError(f"could not retrieve requester example ({uri_val})"), "answer_example_uri")
],
ExampleResourceModel
) from e
except ValidationError as e:
raise ValidationError(
f"requester example image for {uri_val} has unsupported type",
[
ErrorWrapper(
ValueError(f"requester example image for {uri_val} has unsupported type"),
"answer_example_uri"
)
],
ExampleResourceModel
) from e

17 changes: 15 additions & 2 deletions basemodels/manifest/data/requester_restricted_answer_set.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from pydantic.error_wrappers import ErrorWrapper
from requests import RequestException
from pydantic import ValidationError
from .helpers import validate_content_type, ExampleResourceModel
Expand All @@ -23,11 +24,23 @@ def validate_requester_restricted_answer_set_uris(restricted_answer_set: dict) -
validate_content_type(uri)
except RequestException as e:
raise ValidationError(
f"could not retrieve requester restricted answer set example uri ({uri})",
[
ErrorWrapper(
ValueError(f"could not retrieve requester restricted answer set example uri ({uri})"),
"answer_example_uri"
)
],
ExampleResourceModel
) from e
except ValidationError as e:

raise ValidationError(
f"requester restricted answer set example uri ({uri}) content type failed validation",
[
ErrorWrapper(
ValueError(f"requester restricted answer set example uri "
f"({uri}) content type failed validation"),
"answer_example_uri"
)
],
ExampleResourceModel
) from e
17 changes: 14 additions & 3 deletions basemodels/manifest/data/taskdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import requests
from pydantic import BaseModel, HttpUrl, validate_model, ValidationError, validator, root_validator
from pydantic.error_wrappers import ErrorWrapper
from requests import RequestException

from basemodels.constants import SUPPORTED_CONTENT_TYPES
Expand Down Expand Up @@ -77,13 +78,23 @@ def validate_content_type(uri: str) -> None:
response = requests.head(uri)
response.raise_for_status()
except RequestException as e:
raise ValidationError(f"taskdata content type ({uri}) validation failed", TaskDataEntry()) from e
raise ValidationError(
[
ErrorWrapper(ValueError(f"taskdata content type ({uri}) validation failed"), "datapoint_uri")
],
TaskDataEntry
) from e

content_type = response.headers.get("Content-Type", "")
if content_type not in SUPPORTED_CONTENT_TYPES:
raise ValidationError(
f"taskdata entry datapoint_uri has unsupported type {content_type}",
TaskDataEntry(),
[
ErrorWrapper(
ValueError(f"taskdata entry datapoint_uri has unsupported type {content_type}"),
"datapoint_uri"
)
],
TaskDataEntry
)


Expand Down
47 changes: 34 additions & 13 deletions basemodels/manifest/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from .data.requester_restricted_answer_set import validate_requester_restricted_answer_set_uris
from .data.taskdata import validate_taskdata_entry
from pydantic import BaseModel, validator, ValidationError, validate_model, HttpUrl, AnyHttpUrl, root_validator
from pydantic.error_wrappers import ErrorWrapper
from pydantic.fields import Field
from decimal import Decimal
from basemodels.manifest.restricted_audience import RestrictedAudience
Expand Down Expand Up @@ -116,7 +117,7 @@ class TaskData(BaseModel):
@validator("datapoint_uri", always=True)
def validate_datapoint_uri(cls, value):
if value and len(value) < 10:
raise ValidationError("datapoint_uri need to be at least 10 char length.")
raise ValueError("datapoint_uri need to be at least 10 char length.")
return value

@root_validator
Expand Down Expand Up @@ -188,18 +189,18 @@ def validate_requester_restricted_answer_set(cls, value, values, **kwargs):

# validation runs before other params, so need to handle missing case
if "request_type" not in values:
raise ValidationError("request_type missing")
raise ValueError("request_type missing")
if values["request_type"] == BaseJobTypesEnum.image_label_area_select:
if not value or len(value.keys()) == 0:
value = {"label": {}}
values["requester_restricted_answer_set"] = value
if values["request_type"] == BaseJobTypesEnum.image_label_multiple_choice:
if not value or len(value.keys()) <= 1:
raise ValidationError(
raise ValueError(
"image_label_multiple_choice needs at least 2+ options in requester_restricted_answer_set"
)
elif len(value.keys()) > 4:
raise ValidationError(
raise ValueError(
"image_label_multiple_choice can not handle more than 4 options requester_restricted_answer_set"
)
return value
Expand All @@ -214,13 +215,13 @@ def validate_requester_restricted_answer_set(cls, value, values, **kwargs):
def validate_requester_question_example(cls, value, values, **kwargs):
# validation runs before other params, so need to handle missing case
if not ("request_type" in values):
raise ValidationError("request_type missing")
raise ValueError("request_type missing")

# based on https://github.com/hCaptcha/hmt-basemodels/issues/27#issuecomment-590706643
supports_lists = [BaseJobTypesEnum.image_label_area_select, BaseJobTypesEnum.image_label_binary]

if isinstance(value, list) and not values["request_type"] in supports_lists:
raise ValidationError("Lists are not allowed in this challenge type")
raise ValueError("Lists are not allowed in this challenge type")
return value

unsafe_content: bool = False
Expand All @@ -235,7 +236,7 @@ def validate_requester_question_example(cls, value, values, **kwargs):
@validator("groundtruth", always=True)
def validate_groundtruth(cls, v, values, **kwargs):
if "groundtruth_uri" in values and "groundtruth" in values:
raise ValidationError("Specify only groundtruth_uri or groundtruth, not both.")
raise ValueError("Specify only groundtruth_uri or groundtruth, not both.")
return v

# Configuration id -- XXX LEGACY
Expand Down Expand Up @@ -348,11 +349,11 @@ def validate_requester_restricted_answer_set(cls, value, values, **kwargs):

if values["request_type"] == BaseJobTypesEnum.image_label_multiple_choice:
if not value or len(value.keys()) <= 1:
raise ValidationError(
raise ValueError(
"image_label_multiple_choice needs at least 2+ options in requester_restricted_answer_set"
)
elif len(value.keys()) > 4:
raise ValidationError(
raise ValueError(
"image_label_multiple_choice can not handle more than 4 options requester_restricted_answer_set"
)
return value
Expand Down Expand Up @@ -417,10 +418,20 @@ def validate_groundtruth_uri(manifest: dict):
validate_image_content_type = False

except (ValidationError, RequestException) as e:
raise ValidationError(f"{uri_key} validation failed: {e}", Manifest) from e
raise ValidationError(
[
ErrorWrapper(ValueError(f"Validation failed for {uri}: {e}"), uri_key)
],
Manifest
) from e

if entries_count == 0:
raise ValidationError(f"fetched {uri_key} is empty", Manifest)
raise ValidationError(
[
ErrorWrapper(ValueError(f"fetched {uri} is empty"), uri_key)
],
Manifest
)


def validate_taskdata_uri(manifest: dict):
Expand All @@ -446,10 +457,20 @@ def validate_taskdata_uri(manifest: dict):
validate_image_content_type = False # We want to validate only first entry for content type

except (ValidationError, RequestException) as e:
raise ValidationError(f"{uri_key} validation failed: {e}", Manifest) from e
raise ValidationError(
[
ErrorWrapper(ValueError(f"Validation failed for {uri}: {e}"), uri_key)
],
Manifest
) from e

if entries_count == 0:
raise ValidationError(f"fetched {uri_key} is empty", Manifest)
raise ValidationError(
[
ErrorWrapper(ValueError(f"fetched {uri} is empty"), uri_key)
],
Manifest
)


def validate_manifest_example_images(manifest: dict):
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "hmt-basemodels"
version = "0.2.2"
version = "0.2.3"
description = ""
authors = ["Intuition Machines, Inc <[email protected]>"]
packages = [
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setuptools.setup(
name="hmt-basemodels",
version="0.2.2",
version="0.2.3",
author="HUMAN Protocol",
description="Common data models shared by various components of the Human Protocol stack",
url="https://github.com/hCaptcha/hmt-basemodels",
Expand Down
Loading