From 1ffb885f0b0ba661fc3462e589f53280413e118f Mon Sep 17 00:00:00 2001 From: Kilian Bartz Date: Tue, 26 Mar 2024 12:26:39 +0000 Subject: [PATCH 01/10] added pydantic validation --- cbrkit/loaders.py | 85 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/cbrkit/loaders.py b/cbrkit/loaders.py index 81bd48e..bdefad1 100644 --- a/cbrkit/loaders.py +++ b/cbrkit/loaders.py @@ -1,3 +1,31 @@ +""" +To manually use Pydantic with CBRkit to validate your case base, you can use an appropriate +Pydantic model instead of the CBRkit loaders (see example below). +Alternatively, the dataframe, path, file and folder accept an optional validation_model argument +to validate the Casebase entries. + + +Example: + >>> from pydantic import BaseModel, PositiveInt, NonNegativeInt + >>> from typing import Literal + >>> data = csv("data/cars-1k.csv") + + >>> class Car(BaseModel): + >>> price: NonNegativeInt + >>> year: NonNegativeInt + >>> manufacturer: str + >>> make: str + >>> fuel: Literal["gas", "diesel"] + >>> miles: NonNegativeInt + >>> title_status: Literal["clean", "rebuilt"] + >>> transmission: Literal["automatic", "manual"] + >>> drive: Literal["fwd", "rwd", "4wd"] + >>> type: str + >>> paint_color: str + >>> for item in data.values(): + >>> Car.model_validate(item) +""" + import csv as csvlib import tomllib from collections import abc @@ -13,6 +41,7 @@ from pandas import DataFrame, Series from cbrkit.typing import Casebase, FilePath +from pydantic import BaseModel __all__ = [ "csv", @@ -75,12 +104,12 @@ def __len__(self) -> int: return len(self.df) -def dataframe(df: DataFrame) -> Casebase[Any, pd.Series]: +def dataframe(df: DataFrame, validation_model: BaseModel | None = None) -> Casebase[Any, pd.Series]: """Converts a pandas DataFrame into a Casebase. Args: df: pandas DataFrame. - + validation_model: optional Pydantic model to validate the DataFrame. Throws a Pydantic ValidationError if the DataFrame does not match the model. Returns: Returns a Casebase as a DataFrameCasebase. @@ -88,7 +117,26 @@ def dataframe(df: DataFrame) -> Casebase[Any, pd.Series]: >>> file_path = "./data/cars-1k.csv" >>> df = pd.read_csv(file_path) >>> result = dataframe(df) + + >>> from pydantic import BaseModel, PositiveInt, NonNegativeInt + >>> class Car(BaseModel): + >>> price: NonNegativeInt + >>> year: NonNegativeInt + >>> manufacturer: str + >>> make: str + >>> fuel: Literal["gas", "diesel"] + >>> miles: NonNegativeInt + >>> title_status: Literal["clean", "rebuilt"] + >>> transmission: Literal["automatic", "manual"] + >>> drive: Literal["fwd", "rwd", "4wd"] + >>> type: str + >>> paint_color: str + >>> file_path = "./data/cars-1k.csv" + >>> df = pd.read_csv(file_path) + >>> result = dataframe(df, validation_model=Car) """ + if validation_model: + validation_model.model_validate(df.to_dict(orient="records")) return DataFrameCasebase(df) @@ -312,12 +360,12 @@ def path(path: FilePath, pattern: str | None = None) -> Casebase[Any, Any]: return cb -def file(path: Path) -> Casebase[Any, Any] | None: +def file(path: Path, validation_model: BaseModel | None = None) -> Casebase[Any, Any] | None: """Converts a file into a Casebase. The file can be of type csv, json, toml, yaml, or yml. Args: path: Path of the file. - + validation_model: optional Pydantic model to validate the entries of the Casebase. Throws a Pydantic ValidationError if the file does not match the model. Returns: Returns a Casebase. @@ -325,6 +373,25 @@ def file(path: Path) -> Casebase[Any, Any] | None: >>> from pathlib import Path >>> file_path = Path("./data/cars-1k.csv") >>> result = file(file_path) + + >>> from pydantic import BaseModel, PositiveInt, NonNegativeInt + + >>> class Car(BaseModel): + >>> price: NonNegativeInt + >>> year: NonNegativeInt + >>> manufacturer: str + >>> make: str + >>> fuel: Literal["gas", "diesel"] + >>> miles: NonNegativeInt + >>> title_status: Literal["clean", "rebuilt"] + >>> transmission: Literal["automatic", "manual"] + >>> drive: Literal["fwd", "rwd", "4wd"] + >>> type: str + >>> paint_color: str + >>> from pathlib import Path + >>> file_path = Path("./data/cars-1k.csv") + >>> result = file(file_path, validation_model=Car) + """ if path.suffix not in _batch_loaders: return None @@ -332,16 +399,20 @@ def file(path: Path) -> Casebase[Any, Any] | None: loader = _batch_loaders[path.suffix] cb = loader(path) + if validation_model: + for item in cb.values(): + validation_model.model_validate(item) + return cb -def folder(path: Path, pattern: str) -> Casebase[Any, Any] | None: +def folder(path: Path, pattern: str, validation_model: BaseModel | None = None) -> Casebase[Any, Any] | None: """Converts the files of a folder into a Casebase. The files can be of type txt, csv, json, toml, yaml, or yml. Args: path: Path of the folder. pattern: Relative pattern for the files. - + validation_model: optional Pydantic model to validate the entries of the Casebase. Throws a Pydantic ValidationError if the files do not match the model. Returns: Returns a Casebase. @@ -356,6 +427,8 @@ def folder(path: Path, pattern: str) -> Casebase[Any, Any] | None: if file.is_file() and file.suffix in _single_loaders: loader = _single_loaders[path.suffix] cb[file.name] = loader(file) + if validation_model: + validation_model.model_validate(cb[file.name]) if len(cb) == 0: return None From 389ada94a082d111c0d12df8a22d1ada6b34879d Mon Sep 17 00:00:00 2001 From: Kilian Bartz Date: Tue, 26 Mar 2024 12:26:57 +0000 Subject: [PATCH 02/10] black formatting --- cbrkit/loaders.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cbrkit/loaders.py b/cbrkit/loaders.py index bdefad1..0211d2b 100644 --- a/cbrkit/loaders.py +++ b/cbrkit/loaders.py @@ -104,7 +104,9 @@ def __len__(self) -> int: return len(self.df) -def dataframe(df: DataFrame, validation_model: BaseModel | None = None) -> Casebase[Any, pd.Series]: +def dataframe( + df: DataFrame, validation_model: BaseModel | None = None +) -> Casebase[Any, pd.Series]: """Converts a pandas DataFrame into a Casebase. Args: @@ -360,7 +362,9 @@ def path(path: FilePath, pattern: str | None = None) -> Casebase[Any, Any]: return cb -def file(path: Path, validation_model: BaseModel | None = None) -> Casebase[Any, Any] | None: +def file( + path: Path, validation_model: BaseModel | None = None +) -> Casebase[Any, Any] | None: """Converts a file into a Casebase. The file can be of type csv, json, toml, yaml, or yml. Args: @@ -406,7 +410,9 @@ def file(path: Path, validation_model: BaseModel | None = None) -> Casebase[Any, return cb -def folder(path: Path, pattern: str, validation_model: BaseModel | None = None) -> Casebase[Any, Any] | None: +def folder( + path: Path, pattern: str, validation_model: BaseModel | None = None +) -> Casebase[Any, Any] | None: """Converts the files of a folder into a Casebase. The files can be of type txt, csv, json, toml, yaml, or yml. Args: From d24df32a062f48df488512618899729e021083e8 Mon Sep 17 00:00:00 2001 From: Kilian Bartz Date: Thu, 28 Mar 2024 13:11:31 +0000 Subject: [PATCH 03/10] separate validation function --- cbrkit/loaders.py | 115 ++++++++++++++-------------------- data/cars_validation_model.py | 16 +++++ 2 files changed, 64 insertions(+), 67 deletions(-) create mode 100644 data/cars_validation_model.py diff --git a/cbrkit/loaders.py b/cbrkit/loaders.py index 0211d2b..ae8d6a6 100644 --- a/cbrkit/loaders.py +++ b/cbrkit/loaders.py @@ -7,23 +7,10 @@ Example: >>> from pydantic import BaseModel, PositiveInt, NonNegativeInt - >>> from typing import Literal + >>> from data.cars_validation_model import Car >>> data = csv("data/cars-1k.csv") - - >>> class Car(BaseModel): - >>> price: NonNegativeInt - >>> year: NonNegativeInt - >>> manufacturer: str - >>> make: str - >>> fuel: Literal["gas", "diesel"] - >>> miles: NonNegativeInt - >>> title_status: Literal["clean", "rebuilt"] - >>> transmission: Literal["automatic", "manual"] - >>> drive: Literal["fwd", "rwd", "4wd"] - >>> type: str - >>> paint_color: str - >>> for item in data.values(): - >>> Car.model_validate(item) + >>> for row in data.values(): + ... assert isinstance(Car.model_validate(row), Car) """ import csv as csvlib @@ -55,6 +42,7 @@ "python", "txt", "xml", + "validate", ] @@ -104,14 +92,11 @@ def __len__(self) -> int: return len(self.df) -def dataframe( - df: DataFrame, validation_model: BaseModel | None = None -) -> Casebase[Any, pd.Series]: +def dataframe(df: DataFrame) -> Casebase[Any, pd.Series]: """Converts a pandas DataFrame into a Casebase. Args: df: pandas DataFrame. - validation_model: optional Pydantic model to validate the DataFrame. Throws a Pydantic ValidationError if the DataFrame does not match the model. Returns: Returns a Casebase as a DataFrameCasebase. @@ -120,25 +105,10 @@ def dataframe( >>> df = pd.read_csv(file_path) >>> result = dataframe(df) - >>> from pydantic import BaseModel, PositiveInt, NonNegativeInt - >>> class Car(BaseModel): - >>> price: NonNegativeInt - >>> year: NonNegativeInt - >>> manufacturer: str - >>> make: str - >>> fuel: Literal["gas", "diesel"] - >>> miles: NonNegativeInt - >>> title_status: Literal["clean", "rebuilt"] - >>> transmission: Literal["automatic", "manual"] - >>> drive: Literal["fwd", "rwd", "4wd"] - >>> type: str - >>> paint_color: str >>> file_path = "./data/cars-1k.csv" >>> df = pd.read_csv(file_path) - >>> result = dataframe(df, validation_model=Car) + >>> result = dataframe(df) """ - if validation_model: - validation_model.model_validate(df.to_dict(orient="records")) return DataFrameCasebase(df) @@ -362,14 +332,11 @@ def path(path: FilePath, pattern: str | None = None) -> Casebase[Any, Any]: return cb -def file( - path: Path, validation_model: BaseModel | None = None -) -> Casebase[Any, Any] | None: +def file(path: Path) -> Casebase[Any, Any] | None: """Converts a file into a Casebase. The file can be of type csv, json, toml, yaml, or yml. Args: path: Path of the file. - validation_model: optional Pydantic model to validate the entries of the Casebase. Throws a Pydantic ValidationError if the file does not match the model. Returns: Returns a Casebase. @@ -379,22 +346,9 @@ def file( >>> result = file(file_path) >>> from pydantic import BaseModel, PositiveInt, NonNegativeInt - - >>> class Car(BaseModel): - >>> price: NonNegativeInt - >>> year: NonNegativeInt - >>> manufacturer: str - >>> make: str - >>> fuel: Literal["gas", "diesel"] - >>> miles: NonNegativeInt - >>> title_status: Literal["clean", "rebuilt"] - >>> transmission: Literal["automatic", "manual"] - >>> drive: Literal["fwd", "rwd", "4wd"] - >>> type: str - >>> paint_color: str - >>> from pathlib import Path - >>> file_path = Path("./data/cars-1k.csv") - >>> result = file(file_path, validation_model=Car) + >>> from pathlib import Path + >>> file_path = Path("./data/cars-1k.csv") + >>> result = file(file_path) """ if path.suffix not in _batch_loaders: @@ -403,27 +357,21 @@ def file( loader = _batch_loaders[path.suffix] cb = loader(path) - if validation_model: - for item in cb.values(): - validation_model.model_validate(item) - return cb -def folder( - path: Path, pattern: str, validation_model: BaseModel | None = None -) -> Casebase[Any, Any] | None: +def folder(path: Path, pattern: str) -> Casebase[Any, Any] | None: """Converts the files of a folder into a Casebase. The files can be of type txt, csv, json, toml, yaml, or yml. Args: path: Path of the folder. pattern: Relative pattern for the files. - validation_model: optional Pydantic model to validate the entries of the Casebase. Throws a Pydantic ValidationError if the files do not match the model. Returns: Returns a Casebase. Examples: >>> from pathlib import Path + >>> from data.cars_validation_model import Car >>> folder_path = Path("./data") >>> result = folder(folder_path, ".csv") """ @@ -431,12 +379,45 @@ def folder( for file in path.glob(pattern): if file.is_file() and file.suffix in _single_loaders: - loader = _single_loaders[path.suffix] + loader = _single_loaders.get( + file.suffix + ) # prevents a KeyError which can arise for empty key cb[file.name] = loader(file) - if validation_model: - validation_model.model_validate(cb[file.name]) if len(cb) == 0: return None return cb + + +def validate(data: dict[str, Any] | object, validation_model: BaseModel): + """Validates the data against a Pydantic model. Throws a ValueError if data is None or a Pydantic ValidationError if the data does not match the model. + + Args: + data: Data to validate. Can be an entire case base or a single case. + validation_model: Pydantic model to validate the data. + + Examples: + >>> from pydantic import BaseModel, PositiveInt, NonNegativeInt + >>> from data.cars_validation_model import Car + >>> from pathlib import Path + >>> data = path(Path("data/cars-1k.csv")) + >>> validate(data, Car) + # no error is raised if the data is valid + >>> import pandas as pd + >>> df = pd.read_csv("data/cars-1k.csv") + >>> data = dataframe(df) + >>> validate(data, Car) + # no error is raised if the data is valid + """ + if data is None: + raise ValueError("Data is None") + if isinstance(data, DataFrameCasebase): + data = data.df.to_dict("index") + if isinstance(data, dict): + for item in data.values(): + model = validation_model.model_validate(item) + assert isinstance(model, validation_model) + else: + validation_model.model_validate(data) + assert isinstance(data, validation_model) diff --git a/data/cars_validation_model.py b/data/cars_validation_model.py new file mode 100644 index 0000000..c603ea0 --- /dev/null +++ b/data/cars_validation_model.py @@ -0,0 +1,16 @@ +"""This module contains the Pydantic model for validating the car data.""" +from pydantic import BaseModel, PositiveInt, NonNegativeInt +from typing import Literal + +class Car(BaseModel): + price: NonNegativeInt + year: NonNegativeInt + manufacturer: str + make: str + fuel: Literal["gas", "diesel"] + miles: NonNegativeInt + title_status: Literal["clean", "rebuilt"] + transmission: Literal["automatic", "manual"] + drive: Literal["fwd", "rwd", "4wd"] + type: str + paint_color: str From fe790972d093eb0a7e01f3d4cc253f831db928e5 Mon Sep 17 00:00:00 2001 From: Kilian Bartz Date: Thu, 28 Mar 2024 13:18:53 +0000 Subject: [PATCH 04/10] reverted change for folder loader --- cbrkit/loaders.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cbrkit/loaders.py b/cbrkit/loaders.py index ae8d6a6..7466b42 100644 --- a/cbrkit/loaders.py +++ b/cbrkit/loaders.py @@ -373,15 +373,14 @@ def folder(path: Path, pattern: str) -> Casebase[Any, Any] | None: >>> from pathlib import Path >>> from data.cars_validation_model import Car >>> folder_path = Path("./data") - >>> result = folder(folder_path, ".csv") + >>> result = folder(folder_path, "*.csv") + >>> assert result is not None """ cb: Casebase[Any, Any] = {} for file in path.glob(pattern): if file.is_file() and file.suffix in _single_loaders: - loader = _single_loaders.get( - file.suffix - ) # prevents a KeyError which can arise for empty key + loader = _single_loaders[file.suffix] cb[file.name] = loader(file) if len(cb) == 0: @@ -403,12 +402,10 @@ def validate(data: dict[str, Any] | object, validation_model: BaseModel): >>> from pathlib import Path >>> data = path(Path("data/cars-1k.csv")) >>> validate(data, Car) - # no error is raised if the data is valid >>> import pandas as pd >>> df = pd.read_csv("data/cars-1k.csv") >>> data = dataframe(df) >>> validate(data, Car) - # no error is raised if the data is valid """ if data is None: raise ValueError("Data is None") From c67ca331374dd7ffd894971e97d7a24b5cf87cad Mon Sep 17 00:00:00 2001 From: Kilian Bartz Date: Thu, 28 Mar 2024 13:30:46 +0000 Subject: [PATCH 05/10] cleaned up docs --- cbrkit/loaders.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cbrkit/loaders.py b/cbrkit/loaders.py index 7466b42..e7dfda1 100644 --- a/cbrkit/loaders.py +++ b/cbrkit/loaders.py @@ -104,10 +104,6 @@ def dataframe(df: DataFrame) -> Casebase[Any, pd.Series]: >>> file_path = "./data/cars-1k.csv" >>> df = pd.read_csv(file_path) >>> result = dataframe(df) - - >>> file_path = "./data/cars-1k.csv" - >>> df = pd.read_csv(file_path) - >>> result = dataframe(df) """ return DataFrameCasebase(df) From dfe23a45f66cf94287ca246d6e24ff8227f4e21f Mon Sep 17 00:00:00 2001 From: Kilian Bartz Date: Thu, 28 Mar 2024 13:34:20 +0000 Subject: [PATCH 06/10] added pydantic dep --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index cd8925c..84696a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,6 +55,7 @@ transformers = { version = "^4.35", optional = true } typer = { version = ">=0.9, <1.0", extras = ["all"], optional = true } uvicorn = { version = ">=0.24, <1.0", optional = true, extras = ["standard"] } xmltodict = ">=0.13, <1.0" +pydantic = { version = ">=2.0.0", optional = true } [tool.poetry.group.dev.dependencies] pytest = "^8.0.0" From 7b0b43e34b5aa16e53574a8092a5010041f027bd Mon Sep 17 00:00:00 2001 From: Kilian Bartz Date: Thu, 28 Mar 2024 13:37:34 +0000 Subject: [PATCH 07/10] removed unnecessary assertion --- cbrkit/loaders.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cbrkit/loaders.py b/cbrkit/loaders.py index e7dfda1..aca4eed 100644 --- a/cbrkit/loaders.py +++ b/cbrkit/loaders.py @@ -409,8 +409,6 @@ def validate(data: dict[str, Any] | object, validation_model: BaseModel): data = data.df.to_dict("index") if isinstance(data, dict): for item in data.values(): - model = validation_model.model_validate(item) - assert isinstance(model, validation_model) + validation_model.model_validate(item) else: validation_model.model_validate(data) - assert isinstance(data, validation_model) From e1045aa1c8251f4c0929d972bad3e2176b653a4f Mon Sep 17 00:00:00 2001 From: Kilian Bartz Date: Thu, 28 Mar 2024 13:40:15 +0000 Subject: [PATCH 08/10] fixed docs formatting --- cbrkit/loaders.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cbrkit/loaders.py b/cbrkit/loaders.py index aca4eed..6e82bb5 100644 --- a/cbrkit/loaders.py +++ b/cbrkit/loaders.py @@ -97,6 +97,7 @@ def dataframe(df: DataFrame) -> Casebase[Any, pd.Series]: Args: df: pandas DataFrame. + Returns: Returns a Casebase as a DataFrameCasebase. @@ -333,6 +334,7 @@ def file(path: Path) -> Casebase[Any, Any] | None: Args: path: Path of the file. + Returns: Returns a Casebase. @@ -362,6 +364,7 @@ def folder(path: Path, pattern: str) -> Casebase[Any, Any] | None: Args: path: Path of the folder. pattern: Relative pattern for the files. + Returns: Returns a Casebase. From 0480051b77907ea31f4bed5e4e23589c088a6488 Mon Sep 17 00:00:00 2001 From: Kilian Bartz Date: Tue, 2 Apr 2024 06:54:45 +0000 Subject: [PATCH 09/10] suggested changes from pr --- cbrkit/loaders.py | 29 ++++++----------------------- pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/cbrkit/loaders.py b/cbrkit/loaders.py index 6e82bb5..94bb1b5 100644 --- a/cbrkit/loaders.py +++ b/cbrkit/loaders.py @@ -1,16 +1,5 @@ """ -To manually use Pydantic with CBRkit to validate your case base, you can use an appropriate -Pydantic model instead of the CBRkit loaders (see example below). -Alternatively, the dataframe, path, file and folder accept an optional validation_model argument -to validate the Casebase entries. - - -Example: - >>> from pydantic import BaseModel, PositiveInt, NonNegativeInt - >>> from data.cars_validation_model import Car - >>> data = csv("data/cars-1k.csv") - >>> for row in data.values(): - ... assert isinstance(Car.model_validate(row), Car) +This module provides several loaders to read data from different file formats and convert it into a Casebase. To validate the data against a Pydantic model, a `validate` function is also provided. """ import csv as csvlib @@ -19,7 +8,7 @@ from collections.abc import Callable, Iterator from importlib import import_module from pathlib import Path -from typing import Any, cast +from typing import Any, cast, Mapping import orjson import pandas as pd @@ -343,11 +332,6 @@ def file(path: Path) -> Casebase[Any, Any] | None: >>> file_path = Path("./data/cars-1k.csv") >>> result = file(file_path) - >>> from pydantic import BaseModel, PositiveInt, NonNegativeInt - >>> from pathlib import Path - >>> file_path = Path("./data/cars-1k.csv") - >>> result = file(file_path) - """ if path.suffix not in _batch_loaders: return None @@ -364,13 +348,12 @@ def folder(path: Path, pattern: str) -> Casebase[Any, Any] | None: Args: path: Path of the folder. pattern: Relative pattern for the files. - + Returns: Returns a Casebase. Examples: >>> from pathlib import Path - >>> from data.cars_validation_model import Car >>> folder_path = Path("./data") >>> result = folder(folder_path, "*.csv") >>> assert result is not None @@ -388,7 +371,7 @@ def folder(path: Path, pattern: str) -> Casebase[Any, Any] | None: return cb -def validate(data: dict[str, Any] | object, validation_model: BaseModel): +def validate(data: Casebase[Any, Any] | Any, validation_model: BaseModel): """Validates the data against a Pydantic model. Throws a ValueError if data is None or a Pydantic ValidationError if the data does not match the model. Args: @@ -408,9 +391,9 @@ def validate(data: dict[str, Any] | object, validation_model: BaseModel): """ if data is None: raise ValueError("Data is None") - if isinstance(data, DataFrameCasebase): + elif isinstance(data, DataFrameCasebase): data = data.df.to_dict("index") - if isinstance(data, dict): + if isinstance(data, Mapping): for item in data.values(): validation_model.model_validate(item) else: diff --git a/pyproject.toml b/pyproject.toml index 84696a1..7afcb50 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,7 @@ transformers = { version = "^4.35", optional = true } typer = { version = ">=0.9, <1.0", extras = ["all"], optional = true } uvicorn = { version = ">=0.24, <1.0", optional = true, extras = ["standard"] } xmltodict = ">=0.13, <1.0" -pydantic = { version = ">=2.0.0", optional = true } +pydantic = "^2.0" [tool.poetry.group.dev.dependencies] pytest = "^8.0.0" From 94f3207e8ad1e090c66639cfc18e29b4e6551c77 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 Apr 2024 06:56:26 +0000 Subject: [PATCH 10/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data/cars_validation_model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/cars_validation_model.py b/data/cars_validation_model.py index c603ea0..552fa3c 100644 --- a/data/cars_validation_model.py +++ b/data/cars_validation_model.py @@ -1,7 +1,9 @@ """This module contains the Pydantic model for validating the car data.""" + from pydantic import BaseModel, PositiveInt, NonNegativeInt from typing import Literal + class Car(BaseModel): price: NonNegativeInt year: NonNegativeInt