Skip to content

Commit

Permalink
chore: kolena client support for pydantic v1 (#651)
Browse files Browse the repository at this point in the history
  • Loading branch information
yifanwu-kolena committed Jul 19, 2024
1 parent ad9fa33 commit 59a2046
Show file tree
Hide file tree
Showing 42 changed files with 174 additions and 113 deletions.
2 changes: 1 addition & 1 deletion kolena/_api/v1/batched_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.
from enum import Enum

from pydantic.dataclasses import dataclass
from kolena._utils.pydantic_v1.dataclasses import dataclass


class BatchedLoad:
Expand Down
2 changes: 1 addition & 1 deletion kolena/_api/v1/client_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.
from enum import Enum

from pydantic.dataclasses import dataclass
from kolena._utils.pydantic_v1.dataclasses import dataclass


class ClientLog:
Expand Down
10 changes: 8 additions & 2 deletions kolena/_api/v1/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@
from typing import Optional
from typing import Set

from pydantic.dataclasses import dataclass

from kolena._api.v1.batched_load import BatchedLoad
from kolena._utils.pydantic_v1.dataclasses import dataclass


class Model:
Expand Down Expand Up @@ -196,3 +195,10 @@ class TestRun:
@dataclass(frozen=True)
class MarkCrashedRequest:
test_run_id: int


Model.LoadAllResponse.__pydantic_model__.update_forward_refs() # type: ignore
TestCase.SingleProcessResponse.__pydantic_model__.update_forward_refs() # type: ignore[attr-defined]
TestCase.BulkProcessRequest.__pydantic_model__.update_forward_refs() # type: ignore[attr-defined]
TestCase.BulkProcessResponse.__pydantic_model__.update_forward_refs() # type: ignore[attr-defined]
TestSuite.LoadAllResponse.__pydantic_model__.update_forward_refs() # type: ignore
10 changes: 5 additions & 5 deletions kolena/_api/v1/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
from typing import Optional
from typing import Union

from pydantic.dataclasses import dataclass
from pydantic.types import StrictBool
from pydantic.types import StrictFloat
from pydantic.types import StrictInt
from pydantic.types import StrictStr
from kolena._utils.pydantic_v1 import StrictBool
from kolena._utils.pydantic_v1 import StrictFloat
from kolena._utils.pydantic_v1 import StrictInt
from kolena._utils.pydantic_v1 import StrictStr
from kolena._utils.pydantic_v1.dataclasses import dataclass


class EventAPI:
Expand Down
10 changes: 8 additions & 2 deletions kolena/_api/v1/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@
from typing import List
from typing import Optional

from pydantic.dataclasses import dataclass

from kolena._api.v1.batched_load import BatchedLoad
from kolena._utils.pydantic_v1.dataclasses import dataclass


class Model:
Expand Down Expand Up @@ -182,3 +181,10 @@ class UploadEmbeddingsRequest(BatchedLoad.WithLoadUUID):
@dataclass(frozen=True)
class UploadEmbeddingsResponse:
n_samples: int


TestRun.CreateOrRetrieveRequest.__pydantic_model__.update_forward_refs() # type: ignore
TestRun.UploadTestSampleMetricsRequest.__pydantic_model__.update_forward_refs() # type: ignore
TestRun.UploadTestSampleThresholdedMetricsRequest.__pydantic_model__.update_forward_refs() # type: ignore
Workflow.EvaluatorResponse.__pydantic_model__.update_forward_refs() # type: ignore
Workflow.ListEvaluatorsResponse.__pydantic_model__.update_forward_refs() # type: ignore
2 changes: 1 addition & 1 deletion kolena/_api/v1/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.
from enum import Enum

from pydantic.dataclasses import dataclass
from kolena._utils.pydantic_v1.dataclasses import dataclass


@dataclass(frozen=True)
Expand Down
5 changes: 2 additions & 3 deletions kolena/_api/v2/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@
from typing import List
from typing import Optional

from pydantic import conint
from pydantic.dataclasses import dataclass

from kolena._api.v1.batched_load import BatchedLoad
from kolena._utils.pydantic_v1 import conint
from kolena._utils.pydantic_v1.dataclasses import dataclass


class Path(str, Enum):
Expand Down
3 changes: 1 addition & 2 deletions kolena/_api/v2/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@
from typing import Optional
from typing import Union

from pydantic.dataclasses import dataclass

from kolena._api.v1.batched_load import BatchedLoad
from kolena._utils.pydantic_v1.dataclasses import dataclass


class Path(str, Enum):
Expand Down
3 changes: 1 addition & 2 deletions kolena/_api/v2/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@
# limitations under the License.
from enum import Enum

from pydantic.dataclasses import dataclass

from kolena._api.v1.batched_load import BatchedLoad
from kolena._utils.pydantic_v1.dataclasses import dataclass


class Path(str, Enum):
Expand Down
3 changes: 1 addition & 2 deletions kolena/_experimental/instance_segmentation/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@
from typing import List
from typing import Union

from pydantic.dataclasses import dataclass

from kolena._experimental.object_detection.workflow import TestSample as BaseTestSample
from kolena._experimental.object_detection.workflow import ThresholdConfiguration
from kolena._utils.pydantic_v1.dataclasses import dataclass
from kolena.workflow import define_workflow
from kolena.workflow import GroundTruth as BaseGroundTruth
from kolena.workflow import Inference as BaseInference
Expand Down
3 changes: 1 addition & 2 deletions kolena/_experimental/object_detection/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
from typing import Optional
from typing import Union

from pydantic.dataclasses import dataclass

from kolena._utils.pydantic_v1.dataclasses import dataclass
from kolena.workflow import define_workflow
from kolena.workflow import EvaluatorConfiguration
from kolena.workflow import GroundTruth as BaseGroundTruth
Expand Down
7 changes: 3 additions & 4 deletions kolena/_utils/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@
import numpy as np
import pandas as pd
import pandera as pa
from pydantic.dataclasses import dataclass
from pydantic.dataclasses import is_pydantic_dataclass

from kolena._utils import log
from kolena._utils.dataframes.validators import validate_df_schema
from kolena._utils.pydantic_v1 import Extra
from kolena._utils.pydantic_v1.dataclasses import dataclass
from kolena._utils.validators import ValidatorConfig


Expand Down Expand Up @@ -72,8 +72,7 @@ def _double_under(input: str) -> bool:
def _allow_extra(cls: Type[T]) -> bool:
# `pydantic.dataclasses.is_built_in_dataclass` would have false-positive when a stdlib-dataclass decorated
# class extends a pydantic dataclass
pydantic_config: dict = getattr(cls, "__pydantic_config__", {})
return is_pydantic_dataclass(cls) and pydantic_config.get("extra", "ignore") == "allow"
return "__pydantic_model__" in vars(cls) and cls.__pydantic_model__.Config.extra == Extra.allow # type: ignore


# used to track data_type string -> TypedDataObject
Expand Down
4 changes: 2 additions & 2 deletions kolena/_utils/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@
from typing import List
from typing import Tuple

from pydantic import validate_call
from shapely.geometry import Polygon
from shapely.validation import make_valid

from kolena._utils.pydantic_v1 import validate_arguments
from kolena._utils.validators import ValidatorConfig


def make_valid_polygon(points: List[Tuple[float, float]]) -> None:
return make_valid(Polygon(points))


@validate_call(config=ValidatorConfig)
@validate_arguments(config=ValidatorConfig)
def validate_polygon(points: List[Tuple[float, float]]) -> None:
try:
make_valid_polygon(points)
Expand Down
7 changes: 3 additions & 4 deletions kolena/_utils/inference_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,17 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from pydantic import validate_call

from kolena._utils.pydantic_v1 import validate_arguments
from kolena._utils.validators import ValidatorConfig


@validate_call(config=ValidatorConfig)
@validate_arguments(config=ValidatorConfig)
def validate_label(label: str) -> None:
if label.strip() == "":
raise ValueError("label must contain non-whitespace characters", label)


@validate_call(config=ValidatorConfig)
@validate_arguments(config=ValidatorConfig)
def validate_confidence(confidence: float) -> None:
if not (0 <= confidence <= 1):
raise ValueError("confidence must be between 0 and 1 (inclusive)", confidence)
25 changes: 25 additions & 0 deletions kolena/_utils/pydantic_v1/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2021-2024 Kolena Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from importlib import metadata

try:
from pydantic.v1 import * # noqa: F401 F403
except ImportError:
from pydantic import * # type: ignore # noqa: F401 F403


try:
_PYDANTIC_MAJOR_VERSION: int = int(metadata.version("pydantic").split(".")[0])
except metadata.PackageNotFoundError:
_PYDANTIC_MAJOR_VERSION = 0
18 changes: 18 additions & 0 deletions kolena/_utils/pydantic_v1/dataclasses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2021-2024 Kolena Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

try:
from pydantic.v1.dataclasses import * # noqa: F401 F403
except ImportError:
from pydantic.dataclasses import * # type: ignore # noqa: F401 F403
13 changes: 6 additions & 7 deletions kolena/_utils/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,15 @@
# limitations under the License.
from typing import Optional

from pydantic import ConfigDict

from kolena._utils.consts import FieldName
from kolena._utils.pydantic_v1 import Extra


# Pydantic configuration for dataclasses and @validate_call decorators
ValidatorConfig = ConfigDict(
arbitrary_types_allowed=True,
extra="allow", # do not fail when unrecognized values are provided
)
# Pydantic configuration for dataclasses and @validate_arguments decorators
class ValidatorConfig:
arbitrary_types_allowed = True
smart_union = True
extra = Extra.allow # do not fail when unrecognized values are provided


def validate_name(field: str, field_name: Optional[FieldName] = None) -> None:
Expand Down
3 changes: 1 addition & 2 deletions kolena/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,10 @@
from typing import List
from typing import Tuple

from pydantic.dataclasses import dataclass

from kolena._utils.datatypes import DataCategory
from kolena._utils.datatypes import DataType
from kolena._utils.datatypes import TypedDataObject
from kolena._utils.pydantic_v1.dataclasses import dataclass
from kolena._utils.validators import ValidatorConfig


Expand Down
3 changes: 1 addition & 2 deletions kolena/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,10 @@
from abc import ABCMeta
from typing import Optional

from pydantic.dataclasses import dataclass

from kolena._utils.datatypes import DataCategory
from kolena._utils.datatypes import DataType
from kolena._utils.datatypes import TypedDataObject
from kolena._utils.pydantic_v1.dataclasses import dataclass
from kolena._utils.validators import ValidatorConfig


Expand Down
2 changes: 1 addition & 1 deletion kolena/metrics/_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
from typing import TypeVar
from typing import Union

from pydantic.dataclasses import dataclass
from shapely.geometry import Polygon as ShapelyPolygon
from shapely.validation import make_valid

from kolena._utils.pydantic_v1.dataclasses import dataclass
from kolena.annotation import BoundingBox
from kolena.annotation import Polygon
from kolena.annotation import ScoredBoundingBox
Expand Down
5 changes: 2 additions & 3 deletions kolena/workflow/define_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
from typing import Tuple
from typing import Type

from pydantic import validate_call

from kolena._utils.pydantic_v1 import validate_arguments
from kolena._utils.validators import ValidatorConfig
from kolena.workflow import GroundTruth
from kolena.workflow import Inference
Expand All @@ -27,7 +26,7 @@
from kolena.workflow import Workflow


@validate_call(config=ValidatorConfig)
@validate_arguments(config=ValidatorConfig)
def define_workflow(
name: str,
test_sample_type: Type[TestSample],
Expand Down
13 changes: 6 additions & 7 deletions kolena/workflow/evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,12 @@
from typing import Tuple
from typing import Type

from pydantic import validate_call
from pydantic.dataclasses import dataclass

from kolena._api.v1.generic import TestRun as API
from kolena._utils.datatypes import DataObject
from kolena._utils.datatypes import get_args
from kolena._utils.datatypes import get_origin
from kolena._utils.pydantic_v1 import validate_arguments
from kolena._utils.pydantic_v1.dataclasses import dataclass
from kolena._utils.validators import ValidatorConfig
from kolena.workflow import GroundTruth
from kolena.workflow import Inference
Expand Down Expand Up @@ -165,7 +164,7 @@ class Evaluator(metaclass=ABCMeta):
configurations: List[EvaluatorConfiguration]
"""The configurations with which to perform evaluation, provided on instantiation."""

@validate_call(config=ValidatorConfig)
@validate_arguments(config=ValidatorConfig)
def __init__(self, configurations: Optional[List[EvaluatorConfiguration]] = None):
if configurations is not None and len(configurations) == 0:
raise ValueError("empty configuration list provided, at least one configuration required or 'None'")
Expand Down Expand Up @@ -221,7 +220,7 @@ def compute_test_case_metrics(
"""
raise NotImplementedError

@validate_call(config=ValidatorConfig)
@validate_arguments(config=ValidatorConfig)
def compute_test_case_plots(
self,
test_case: TestCase,
Expand All @@ -242,7 +241,7 @@ def compute_test_case_plots(
"""
return None # not required

@validate_call(config=ValidatorConfig)
@validate_arguments(config=ValidatorConfig)
def compute_test_suite_metrics(
self,
test_suite: TestSuite,
Expand Down Expand Up @@ -304,7 +303,7 @@ def _maybe_display_name(configuration: Optional[EvaluatorConfiguration]) -> Opti
return configuration.display_name()


@validate_call(config=ValidatorConfig)
@validate_arguments(config=ValidatorConfig)
def _configuration_description(configuration: Optional[EvaluatorConfiguration]) -> str:
display_name = _maybe_display_name(configuration)
return f"(configuration: {display_name})" if display_name is not None else ""
Loading

0 comments on commit 59a2046

Please sign in to comment.