various-api-tools 0.3.2__tar.gz → 0.3.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {various_api_tools-0.3.2 → various_api_tools-0.3.4}/PKG-INFO +3 -2
- {various_api_tools-0.3.2 → various_api_tools-0.3.4}/pyproject.toml +15 -2
- {various_api_tools-0.3.2 → various_api_tools-0.3.4}/src/various_api_tools/__init__.py +4 -0
- various_api_tools-0.3.4/src/various_api_tools/error_maps/__init__.py +1 -0
- various_api_tools-0.3.4/src/various_api_tools/error_maps/pydantic.py +40 -0
- {various_api_tools-0.3.2 → various_api_tools-0.3.4}/src/various_api_tools/translators/pydantic.py +5 -34
- various_api_tools-0.3.4/src/various_api_tools/validators/__init__.py +1 -0
- various_api_tools-0.3.4/src/various_api_tools/validators/pydantic.py +221 -0
- various_api_tools-0.3.4/tests/validators/__init__.py +0 -0
- various_api_tools-0.3.4/tests/validators/test_pydantic.py +120 -0
- {various_api_tools-0.3.2 → various_api_tools-0.3.4}/README.md +0 -0
- {various_api_tools-0.3.2 → various_api_tools-0.3.4}/src/various_api_tools/translators/__init__.py +0 -0
- {various_api_tools-0.3.2 → various_api_tools-0.3.4}/src/various_api_tools/translators/json.py +0 -0
- {various_api_tools-0.3.2 → various_api_tools-0.3.4}/src/various_api_tools/translators/psycopg2.py +0 -0
- {various_api_tools-0.3.2 → various_api_tools-0.3.4}/tests/__init__.py +0 -0
- {various_api_tools-0.3.2 → various_api_tools-0.3.4}/tests/translators/__init__.py +0 -0
- {various_api_tools-0.3.2 → various_api_tools-0.3.4}/tests/translators/test_json.py +0 -0
- {various_api_tools-0.3.2 → various_api_tools-0.3.4}/tests/translators/test_psycopg2.py +0 -0
- {various_api_tools-0.3.2 → various_api_tools-0.3.4}/tests/translators/test_pydantic.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: various-api-tools
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: A lightweight utility package for common API-related tasks in Python, including JSON and Pydantic error translators that provide user-friendly Russian messages.
|
|
5
5
|
Author-Email: dkurchigin <kurchigin.dmitry@yandex.ru>
|
|
6
6
|
License: MIT
|
|
@@ -16,8 +16,9 @@ Project-URL: Homepage, https://gitverse.ru/dkurchigin/various-api-tools
|
|
|
16
16
|
Project-URL: Documentation, https://various-api-tools.dkurchigin.ru/
|
|
17
17
|
Project-URL: Source, https://gitverse.ru/dkurchigin/various-api-tools
|
|
18
18
|
Requires-Python: >=3.10
|
|
19
|
-
Requires-Dist: pydantic>=2.
|
|
19
|
+
Requires-Dist: pydantic>=2.12.3
|
|
20
20
|
Requires-Dist: psycopg2-binary>=2.9.10
|
|
21
|
+
Requires-Dist: email-validator>=2.3.0
|
|
21
22
|
Description-Content-Type: text/markdown
|
|
22
23
|
|
|
23
24
|
# Various_api_tools
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "various-api-tools"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.4"
|
|
4
4
|
description = "A lightweight utility package for common API-related tasks in Python, including JSON and Pydantic error translators that provide user-friendly Russian messages."
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "dkurchigin", email = "kurchigin.dmitry@yandex.ru" },
|
|
7
7
|
]
|
|
8
8
|
dependencies = [
|
|
9
|
-
"pydantic>=2.
|
|
9
|
+
"pydantic>=2.12.3",
|
|
10
10
|
"psycopg2-binary>=2.9.10",
|
|
11
|
+
"email-validator>=2.3.0",
|
|
11
12
|
]
|
|
12
13
|
requires-python = ">=3.10"
|
|
13
14
|
readme = "README.md"
|
|
@@ -40,6 +41,7 @@ build-backend = "pdm.backend"
|
|
|
40
41
|
distribution = true
|
|
41
42
|
|
|
42
43
|
[tool.pdm.scripts]
|
|
44
|
+
isort = "ruff check --select I --fix ."
|
|
43
45
|
test = "pytest --cov-report term-missing --cov=src tests/"
|
|
44
46
|
lint = "ruff check --fix"
|
|
45
47
|
format = "ruff format"
|
|
@@ -144,11 +146,22 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
|
144
146
|
"E",
|
|
145
147
|
"ANN",
|
|
146
148
|
"EM",
|
|
149
|
+
"B",
|
|
147
150
|
"TRY",
|
|
148
151
|
"PT",
|
|
152
|
+
"FBT",
|
|
153
|
+
"SLF",
|
|
149
154
|
]
|
|
150
155
|
"src/various_api_tools/translators/pydantic.py" = [
|
|
151
156
|
"RUF001",
|
|
157
|
+
"TID252",
|
|
158
|
+
]
|
|
159
|
+
"src/various_api_tools/validators/pydantic.py" = [
|
|
160
|
+
"EM101",
|
|
161
|
+
"TID252",
|
|
162
|
+
]
|
|
163
|
+
"src/various_api_tools/error_maps/pydantic.py" = [
|
|
164
|
+
"RUF001",
|
|
152
165
|
]
|
|
153
166
|
|
|
154
167
|
[tool.ruff.format]
|
|
@@ -3,12 +3,16 @@
|
|
|
3
3
|
Including JSON and Pydantic error translators.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
from .error_maps.pydantic import PYDANTIC_ERROR_TYPES
|
|
6
7
|
from .translators.json import JSONDecodeErrorTranslator
|
|
7
8
|
from .translators.psycopg2 import Psycopg2ErrorTranslator
|
|
8
9
|
from .translators.pydantic import PydanticValidationErrorTranslator
|
|
10
|
+
from .validators.pydantic import PydanticValidator
|
|
9
11
|
|
|
10
12
|
__all__ = (
|
|
13
|
+
"PYDANTIC_ERROR_TYPES",
|
|
11
14
|
"JSONDecodeErrorTranslator",
|
|
12
15
|
"Psycopg2ErrorTranslator",
|
|
13
16
|
"PydanticValidationErrorTranslator",
|
|
17
|
+
"PydanticValidator",
|
|
14
18
|
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Module for some error mappings."""
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Module for mapping Pydantic validation error codes to messages in Russian.
|
|
2
|
+
|
|
3
|
+
This module provides a dictionary that maps common Pydantic error types to descriptive
|
|
4
|
+
error messages in Russian, used for user-friendly validation feedback.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Final
|
|
8
|
+
|
|
9
|
+
PYDANTIC_ERROR_TYPES: dict[str, str] = {
|
|
10
|
+
"missing": "Не заполнено обязательное поле",
|
|
11
|
+
"uuid_parsing": "Невалидное значение для UUID",
|
|
12
|
+
"uuid_type": "Невалидное значение для UUID",
|
|
13
|
+
"uuid_version": "Невалидное значение для UUID",
|
|
14
|
+
"bool_parsing": "Невалидное значение для логического типа(bool)",
|
|
15
|
+
"bool_type": "Невалидное значение для логического типа(bool)",
|
|
16
|
+
"date_type": "Невалидное значение даты(date)",
|
|
17
|
+
"datetime_from_date_parsing": "Невалидное значение даты и времени(datetime)",
|
|
18
|
+
"datetime_type": "Невалидное значение даты и времени(datetime)",
|
|
19
|
+
"dict_type": "Невалидное значение словаря",
|
|
20
|
+
"list_type": "Невалидное значение списка",
|
|
21
|
+
"string_type": "Невалидное строковое значение(str)",
|
|
22
|
+
"enum": "Невалидное значение Enum",
|
|
23
|
+
"float_parsing": "Невалидное значение числа с плавающей точкой(float)",
|
|
24
|
+
"float_type": "Невалидное значение числа с плавающей точкой(float)",
|
|
25
|
+
"int_from_float": "Невалидное значение для целочисленного числа(int)",
|
|
26
|
+
"int_parsing": "Невалидное значение для целочисленного числа(int)",
|
|
27
|
+
"int_parsing_size": "Невалидное значение для целочисленного числа(int)",
|
|
28
|
+
"int_type": "Невалидное значение для целочисленного числа(int)",
|
|
29
|
+
"non_negative_int": "Невалидное значение для целочисленного числа(int), "
|
|
30
|
+
"должно быть больше или равно 0",
|
|
31
|
+
"incorrect_latitude": "Некорректное значение широты, должно быть больше, "
|
|
32
|
+
"чем -90.0 и меньше, чем 90.0",
|
|
33
|
+
"incorrect_longitude": "Некорректное значение долготы, должно быть больше, "
|
|
34
|
+
"чем -180.0 и меньше, чем 180.0",
|
|
35
|
+
"string_too_short": "Cтрока слишком короткая",
|
|
36
|
+
"ip_address": "Невалидное значение адреса IPv4/IPv6",
|
|
37
|
+
"incorrect_email": "Невалидное значение email-адреса",
|
|
38
|
+
"list_expected": "Некорректный тип данных. Ожидается список.",
|
|
39
|
+
}
|
|
40
|
+
UNKNOWN_ERROR_TYPE: Final[str] = "Неизвестная ошибка"
|
{various_api_tools-0.3.2 → various_api_tools-0.3.4}/src/various_api_tools/translators/pydantic.py
RENAMED
|
@@ -10,40 +10,9 @@ from typing import Final
|
|
|
10
10
|
|
|
11
11
|
from pydantic_core import ErrorDetails
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
"missing": "Не заполнено обязательное поле",
|
|
15
|
-
"uuid_parsing": "Невалидное значение для UUID",
|
|
16
|
-
"uuid_type": "Невалидное значение для UUID",
|
|
17
|
-
"uuid_version": "Невалидное значение для UUID",
|
|
18
|
-
"bool_parsing": "Невалидное значение для логического типа(bool)",
|
|
19
|
-
"bool_type": "Невалидное значение для логического типа(bool)",
|
|
20
|
-
"date_type": "Невалидное значение даты(date)",
|
|
21
|
-
"datetime_from_date_parsing": "Невалидное значение даты и времени(datetime)",
|
|
22
|
-
"datetime_type": "Невалидное значение даты и времени(datetime)",
|
|
23
|
-
"dict_type": "Невалидное значение словаря",
|
|
24
|
-
"list_type": "Невалидное значение списка",
|
|
25
|
-
"string_type": "Невалидное строковое значение(str)",
|
|
26
|
-
"enum": "Невалидное значение Enum",
|
|
27
|
-
"float_parsing": "Невалидное значение числа с плавающей точкой(float)",
|
|
28
|
-
"float_type": "Невалидное значение числа с плавающей точкой(float)",
|
|
29
|
-
"int_from_float": "Невалидное значение для целочисленного числа(int)",
|
|
30
|
-
"int_parsing": "Невалидное значение для целочисленного числа(int)",
|
|
31
|
-
"int_parsing_size": "Невалидное значение для целочисленного числа(int)",
|
|
32
|
-
"int_type": "Невалидное значение для целочисленного числа(int)",
|
|
33
|
-
"non_negative_int": "Невалидное значение для целочисленного числа(int), "
|
|
34
|
-
"должно быть больше или равно 0",
|
|
35
|
-
"incorrect_latitude": "Некорректное значение широты, должно быть больше, "
|
|
36
|
-
"чем -90.0 и меньше, чем 90.0",
|
|
37
|
-
"incorrect_longitude": "Некорректное значение долготы, должно быть больше, "
|
|
38
|
-
"чем -180.0 и меньше, чем 180.0",
|
|
39
|
-
"string_too_short": "Cтрока слишком короткая",
|
|
40
|
-
"ip_address": "Невалидное значение адреса IPv4/IPv6",
|
|
41
|
-
"incorrect_email": "Невалидное значение email-адреса",
|
|
42
|
-
"list_expected": "Некорректный тип данных. Ожидается список.",
|
|
43
|
-
}
|
|
13
|
+
from ..error_maps.pydantic import PYDANTIC_ERROR_TYPES, UNKNOWN_ERROR_TYPE
|
|
44
14
|
|
|
45
15
|
DEFAULT_LOCATION_PREFIX: Final[str] = "Поле"
|
|
46
|
-
UNKNOWN_ERROR_TYPE: Final[str] = "Неизвестная ошибка"
|
|
47
16
|
|
|
48
17
|
|
|
49
18
|
class PydanticValidationErrorTranslator:
|
|
@@ -54,6 +23,8 @@ class PydanticValidationErrorTranslator:
|
|
|
54
23
|
|
|
55
24
|
"""
|
|
56
25
|
|
|
26
|
+
error_types = PYDANTIC_ERROR_TYPES
|
|
27
|
+
|
|
57
28
|
@classmethod
|
|
58
29
|
def get_str_pydantic_loc(cls, loc: tuple[str]) -> str:
|
|
59
30
|
"""Convert a Pydantic location tuple into a dot-separated string.
|
|
@@ -114,10 +85,10 @@ class PydanticValidationErrorTranslator:
|
|
|
114
85
|
)
|
|
115
86
|
|
|
116
87
|
if "type" in error:
|
|
117
|
-
msg =
|
|
88
|
+
msg = cls.error_types.get(error["type"], UNKNOWN_ERROR_TYPE)
|
|
118
89
|
type_part = f'Ошибка: "{msg}"'
|
|
119
90
|
|
|
120
|
-
if "input" in error and
|
|
91
|
+
if "input" in error and cls.error_types["missing"] not in type_part:
|
|
121
92
|
input_part = f'заполнено неверно: "{error["input"]!r}"'
|
|
122
93
|
|
|
123
94
|
if input_part != "":
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Module for various API validators."""
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""Module for custom Pydantic validators with user-friendly error messages.
|
|
2
|
+
|
|
3
|
+
Provides a class with methods to validate various types of input data,
|
|
4
|
+
raising PydanticCustomError with predefined error descriptions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from decimal import Decimal
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from pydantic import EmailStr
|
|
11
|
+
from pydantic_core._pydantic_core import PydanticCustomError
|
|
12
|
+
|
|
13
|
+
from ..error_maps.pydantic import PYDANTIC_ERROR_TYPES
|
|
14
|
+
|
|
15
|
+
MIN_LATITUDE = -90.0
|
|
16
|
+
MAX_LATITUDE = 90.0
|
|
17
|
+
MIN_LONGITUDE = -180.0
|
|
18
|
+
MAX_LONGITUDE = 180.0
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PydanticValidator:
|
|
22
|
+
"""A class that provides custom validation methods for Pydantic models.
|
|
23
|
+
|
|
24
|
+
These methods are designed to raise PydanticCustomError with specific error codes
|
|
25
|
+
and human-readable error messages for better end-user experience.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
error_types = PYDANTIC_ERROR_TYPES
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def strip_validator(cls, value: str) -> str:
|
|
32
|
+
"""Strip whitespace from both ends of a string.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
value: Input string to be stripped.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
The stripped string.
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
```python
|
|
42
|
+
print(PydanticValidator.strip_validator(" test "))
|
|
43
|
+
#> "test"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
return value.strip()
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def optional_string_validator(cls, value: Any | None) -> str | None: # noqa: ANN401
|
|
51
|
+
"""Validate and strips an optional string value.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
value: Input value to be validated.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
A stripped string or None if the value is empty.
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
PydanticCustomError: If the value is not a string.
|
|
61
|
+
|
|
62
|
+
Example:
|
|
63
|
+
```python
|
|
64
|
+
print(PydanticValidator.optional_string_validator(" test "))
|
|
65
|
+
#> "test"
|
|
66
|
+
|
|
67
|
+
print(PydanticValidator.optional_string_validator(""))
|
|
68
|
+
#> None
|
|
69
|
+
|
|
70
|
+
print(PydanticValidator.optional_string_validator(123))
|
|
71
|
+
#> Traceback (most recent call last):
|
|
72
|
+
#> ...
|
|
73
|
+
#> PydanticCustomError: 'string_type'
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
"""
|
|
77
|
+
if value is not None:
|
|
78
|
+
if not isinstance(value, str):
|
|
79
|
+
raise PydanticCustomError("string_type", cls.error_types["string_type"])
|
|
80
|
+
|
|
81
|
+
value = cls.strip_validator(value=value)
|
|
82
|
+
if value == "":
|
|
83
|
+
value = None
|
|
84
|
+
|
|
85
|
+
return value
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def email_validator(cls, value: str) -> EmailStr | None:
|
|
89
|
+
"""Validate an email address format.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
value: Input string to be validated as an email.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Validated EmailStr object or None if input is invalid.
|
|
96
|
+
|
|
97
|
+
Raises:
|
|
98
|
+
PydanticCustomError: If the email format is incorrect.
|
|
99
|
+
|
|
100
|
+
Example:
|
|
101
|
+
```python
|
|
102
|
+
print(PydanticValidator.email_validator("test@example.com"))
|
|
103
|
+
#> EmailStr('test@example.com')
|
|
104
|
+
|
|
105
|
+
print(PydanticValidator.email_validator("invalid-email"))
|
|
106
|
+
#> Traceback (most recent call last):
|
|
107
|
+
#> ...
|
|
108
|
+
#> PydanticCustomError: 'incorrect_email'
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
"""
|
|
112
|
+
email_str: EmailStr | None = None
|
|
113
|
+
stripped_value: str | None = cls.optional_string_validator(value=value)
|
|
114
|
+
|
|
115
|
+
if stripped_value is not None:
|
|
116
|
+
try:
|
|
117
|
+
email_str = EmailStr._validate(stripped_value) # noqa: SLF001
|
|
118
|
+
except ValueError as exc:
|
|
119
|
+
raise PydanticCustomError(
|
|
120
|
+
"incorrect_email",
|
|
121
|
+
cls.error_types["incorrect_email"],
|
|
122
|
+
) from exc
|
|
123
|
+
|
|
124
|
+
return email_str
|
|
125
|
+
|
|
126
|
+
@classmethod
|
|
127
|
+
def latitude_validator(cls, value: Decimal) -> Decimal:
|
|
128
|
+
"""Validate that a value is a valid geographic latitude.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
value: Input Decimal to be validated.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
The validated Decimal value.
|
|
135
|
+
|
|
136
|
+
Raises:
|
|
137
|
+
PydanticCustomError: If the value is not in the range [-90.0, 90.0].
|
|
138
|
+
|
|
139
|
+
Example:
|
|
140
|
+
```python
|
|
141
|
+
print(PydanticValidator.latitude_validator(Decimal("45.0")))
|
|
142
|
+
#> Decimal('45.0')
|
|
143
|
+
|
|
144
|
+
print(PydanticValidator.latitude_validator(Decimal("100.0")))
|
|
145
|
+
#> Traceback (most recent call last):
|
|
146
|
+
#> ...
|
|
147
|
+
#> PydanticCustomError: 'incorrect_latitude'
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
"""
|
|
151
|
+
if not MIN_LATITUDE <= value <= MAX_LATITUDE:
|
|
152
|
+
raise PydanticCustomError(
|
|
153
|
+
"incorrect_latitude",
|
|
154
|
+
cls.error_types["incorrect_latitude"],
|
|
155
|
+
)
|
|
156
|
+
return value
|
|
157
|
+
|
|
158
|
+
@classmethod
|
|
159
|
+
def longitude_validator(cls, value: Decimal) -> Decimal:
|
|
160
|
+
"""Validate that a value is a valid geographic longitude.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
value: Input Decimal to be validated.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
The validated Decimal value.
|
|
167
|
+
|
|
168
|
+
Raises:
|
|
169
|
+
PydanticCustomError: If the value is not in the range [-180.0, 180.0].
|
|
170
|
+
|
|
171
|
+
Example:
|
|
172
|
+
```python
|
|
173
|
+
print(PydanticValidator.longitude_validator(Decimal("90.0")))
|
|
174
|
+
#> Decimal('90.0')
|
|
175
|
+
|
|
176
|
+
print(PydanticValidator.longitude_validator(Decimal("200.0")))
|
|
177
|
+
#> Traceback (most recent call last):
|
|
178
|
+
#> ...
|
|
179
|
+
#> PydanticCustomError: 'incorrect_longitude'
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
"""
|
|
183
|
+
if not MIN_LONGITUDE <= value <= MAX_LONGITUDE:
|
|
184
|
+
raise PydanticCustomError(
|
|
185
|
+
"incorrect_longitude",
|
|
186
|
+
cls.error_types["incorrect_longitude"],
|
|
187
|
+
)
|
|
188
|
+
return value
|
|
189
|
+
|
|
190
|
+
@classmethod
|
|
191
|
+
def validate_required_field(cls, value: Any | None) -> Any: # noqa: ANN401
|
|
192
|
+
"""Validate that a field is not missing in update models.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
value: Input value to be validated.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
The original value if it's valid.
|
|
199
|
+
|
|
200
|
+
Raises:
|
|
201
|
+
PydanticCustomError: If the value is None or empty string.
|
|
202
|
+
|
|
203
|
+
Example:
|
|
204
|
+
```python
|
|
205
|
+
print(PydanticValidator.validate_required_field("test"))
|
|
206
|
+
#> "test"
|
|
207
|
+
|
|
208
|
+
print(PydanticValidator.validate_required_field(None))
|
|
209
|
+
#> Traceback (most recent call last):
|
|
210
|
+
#> ...
|
|
211
|
+
#> PydanticCustomError: 'missing'
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
"""
|
|
215
|
+
if isinstance(value, str):
|
|
216
|
+
value = cls.optional_string_validator(value=value)
|
|
217
|
+
|
|
218
|
+
if value is None:
|
|
219
|
+
raise PydanticCustomError("missing", cls.error_types["missing"])
|
|
220
|
+
|
|
221
|
+
return value
|
|
File without changes
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
from pydantic import EmailStr
|
|
6
|
+
from pydantic_core._pydantic_core import PydanticCustomError
|
|
7
|
+
|
|
8
|
+
from src.various_api_tools.validators.pydantic import PydanticValidator
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestPydanticValidator:
|
|
12
|
+
@pytest.mark.parametrize(
|
|
13
|
+
"input_value, expected_output",
|
|
14
|
+
[
|
|
15
|
+
(" test ", "test"),
|
|
16
|
+
("", ""),
|
|
17
|
+
(" ", ""),
|
|
18
|
+
],
|
|
19
|
+
)
|
|
20
|
+
def test_strip_validator(self, input_value: str, expected_output: str):
|
|
21
|
+
result = PydanticValidator.strip_validator(input_value)
|
|
22
|
+
assert result == expected_output
|
|
23
|
+
|
|
24
|
+
@pytest.mark.parametrize(
|
|
25
|
+
"input_value, expected_output, is_raised",
|
|
26
|
+
[
|
|
27
|
+
(" test ", "test", False),
|
|
28
|
+
("", None, False),
|
|
29
|
+
(123, None, True),
|
|
30
|
+
],
|
|
31
|
+
)
|
|
32
|
+
def test_optional_string_validator(
|
|
33
|
+
self,
|
|
34
|
+
input_value: Any,
|
|
35
|
+
expected_output: str | None,
|
|
36
|
+
is_raised: bool,
|
|
37
|
+
):
|
|
38
|
+
if is_raised:
|
|
39
|
+
with pytest.raises(Exception):
|
|
40
|
+
PydanticValidator.optional_string_validator(input_value)
|
|
41
|
+
else:
|
|
42
|
+
result = PydanticValidator.optional_string_validator(input_value)
|
|
43
|
+
assert result == expected_output
|
|
44
|
+
|
|
45
|
+
@pytest.mark.parametrize(
|
|
46
|
+
"input_value, expected_output, is_raised",
|
|
47
|
+
[
|
|
48
|
+
("test@example.com", EmailStr._validate("test@example.com"), False),
|
|
49
|
+
(None, None, False),
|
|
50
|
+
("invalid-email", None, True),
|
|
51
|
+
],
|
|
52
|
+
)
|
|
53
|
+
def test_email_validator(
|
|
54
|
+
self, input_value: Any, expected_output: EmailStr | None, is_raised: bool,
|
|
55
|
+
):
|
|
56
|
+
if is_raised:
|
|
57
|
+
with pytest.raises(Exception):
|
|
58
|
+
PydanticValidator.email_validator(input_value)
|
|
59
|
+
else:
|
|
60
|
+
result = PydanticValidator.email_validator(input_value)
|
|
61
|
+
assert result == expected_output
|
|
62
|
+
|
|
63
|
+
@pytest.mark.parametrize(
|
|
64
|
+
"input_value, expected_output, is_raised",
|
|
65
|
+
[
|
|
66
|
+
(Decimal("45.0"), Decimal("45.0"), False),
|
|
67
|
+
(Decimal("90.0"), Decimal("90.0"), False),
|
|
68
|
+
(Decimal("-90.0"), Decimal("-90.0"), False),
|
|
69
|
+
(Decimal("100.0"), None, True),
|
|
70
|
+
],
|
|
71
|
+
)
|
|
72
|
+
def test_latitude_validator(
|
|
73
|
+
self, input_value: Decimal, expected_output: Decimal | None, is_raised: bool,
|
|
74
|
+
):
|
|
75
|
+
if is_raised:
|
|
76
|
+
with pytest.raises(Exception):
|
|
77
|
+
PydanticValidator.latitude_validator(input_value)
|
|
78
|
+
else:
|
|
79
|
+
result = PydanticValidator.latitude_validator(input_value)
|
|
80
|
+
assert result == expected_output
|
|
81
|
+
|
|
82
|
+
@pytest.mark.parametrize(
|
|
83
|
+
"input_value, expected_output, is_raised",
|
|
84
|
+
[
|
|
85
|
+
(Decimal("90.0"), Decimal("90.0"), False),
|
|
86
|
+
(Decimal("-180.0"), Decimal("-180.0"), False),
|
|
87
|
+
(Decimal("180.0"), Decimal("180.0"), False),
|
|
88
|
+
(Decimal("200.0"), None, True),
|
|
89
|
+
],
|
|
90
|
+
)
|
|
91
|
+
def test_longitude_validator(
|
|
92
|
+
self, input_value: Decimal, expected_output: Decimal | None, is_raised: bool,
|
|
93
|
+
):
|
|
94
|
+
if is_raised:
|
|
95
|
+
with pytest.raises(Exception):
|
|
96
|
+
PydanticValidator.longitude_validator(input_value)
|
|
97
|
+
else:
|
|
98
|
+
result = PydanticValidator.longitude_validator(input_value)
|
|
99
|
+
assert result == expected_output
|
|
100
|
+
|
|
101
|
+
@pytest.mark.parametrize(
|
|
102
|
+
"input_value, expected_output, is_raised",
|
|
103
|
+
[
|
|
104
|
+
("test", "test", False),
|
|
105
|
+
(None, pytest.raises(PydanticCustomError), True),
|
|
106
|
+
("", pytest.raises(PydanticCustomError), True),
|
|
107
|
+
],
|
|
108
|
+
)
|
|
109
|
+
def test_validate_required_field(
|
|
110
|
+
self,
|
|
111
|
+
input_value: Any,
|
|
112
|
+
expected_output: Any,
|
|
113
|
+
is_raised: bool,
|
|
114
|
+
):
|
|
115
|
+
if is_raised:
|
|
116
|
+
with pytest.raises(Exception):
|
|
117
|
+
PydanticValidator.validate_required_field(input_value)
|
|
118
|
+
else:
|
|
119
|
+
result = PydanticValidator.validate_required_field(input_value)
|
|
120
|
+
assert result == expected_output
|
|
File without changes
|
{various_api_tools-0.3.2 → various_api_tools-0.3.4}/src/various_api_tools/translators/__init__.py
RENAMED
|
File without changes
|
{various_api_tools-0.3.2 → various_api_tools-0.3.4}/src/various_api_tools/translators/json.py
RENAMED
|
File without changes
|
{various_api_tools-0.3.2 → various_api_tools-0.3.4}/src/various_api_tools/translators/psycopg2.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|