ul-api-utils 8.0.1a0__py3-none-any.whl → 8.0.3__py3-none-any.whl
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.
Potentially problematic release.
This version of ul-api-utils might be problematic. Click here for more details.
- example/routes/api_some.py +1 -1
- ul_api_utils/access/__init__.py +2 -3
- ul_api_utils/api_resource/api_request.py +3 -2
- ul_api_utils/api_resource/api_resource.py +6 -5
- ul_api_utils/api_resource/api_resource_config.py +5 -4
- ul_api_utils/api_resource/api_resource_fn_typing.py +13 -11
- ul_api_utils/api_resource/api_response.py +11 -11
- ul_api_utils/api_resource/signature_check.py +6 -6
- ul_api_utils/commands/cmd_enc_keys.py +5 -5
- ul_api_utils/commands/cmd_gen_new_api_user.py +1 -1
- ul_api_utils/commands/cmd_generate_api_docs.py +1 -1
- ul_api_utils/commands/cmd_start.py +2 -2
- ul_api_utils/debug/debugger.py +1 -1
- ul_api_utils/errors.py +14 -8
- ul_api_utils/internal_api/internal_api_error.py +6 -4
- ul_api_utils/modules/api_sdk.py +2 -2
- ul_api_utils/modules/api_sdk_config.py +6 -6
- ul_api_utils/modules/worker_sdk_config.py +6 -6
- ul_api_utils/resources/caching.py +6 -6
- ul_api_utils/resources/debugger_scripts.py +1 -1
- ul_api_utils/resources/health_check/health_check.py +9 -11
- ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_models.py +2 -2
- ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_three_specifier.py +5 -6
- ul_api_utils/utils/json_encoder.py +5 -5
- ul_api_utils/utils/memory_db/repository.py +4 -4
- ul_api_utils/utils/unwrap_typing.py +2 -2
- ul_api_utils/validators/custom_fields.py +16 -67
- {ul_api_utils-8.0.1a0.dist-info → ul_api_utils-8.0.3.dist-info}/METADATA +7 -9
- {ul_api_utils-8.0.1a0.dist-info → ul_api_utils-8.0.3.dist-info}/RECORD +33 -33
- {ul_api_utils-8.0.1a0.dist-info → ul_api_utils-8.0.3.dist-info}/LICENSE +0 -0
- {ul_api_utils-8.0.1a0.dist-info → ul_api_utils-8.0.3.dist-info}/WHEEL +0 -0
- {ul_api_utils-8.0.1a0.dist-info → ul_api_utils-8.0.3.dist-info}/entry_points.txt +0 -0
- {ul_api_utils-8.0.1a0.dist-info → ul_api_utils-8.0.3.dist-info}/top_level.txt +0 -0
example/routes/api_some.py
CHANGED
ul_api_utils/access/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from typing import Dict, List, Union, Any, Iterable
|
|
3
3
|
|
|
4
|
-
from pydantic import BaseModel
|
|
4
|
+
from pydantic import ConfigDict, BaseModel
|
|
5
5
|
|
|
6
6
|
from ul_api_utils.conf import APPLICATION_F
|
|
7
7
|
|
|
@@ -13,8 +13,7 @@ class PermissionDefinition(BaseModel):
|
|
|
13
13
|
category: str
|
|
14
14
|
flags: str = ''
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
allow_mutation: bool = False
|
|
16
|
+
model_config = ConfigDict(frozen=True)
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
class PermissionRegistry:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from typing import NamedTuple, Iterable, Any, List, Tuple, Dict, Optional
|
|
3
3
|
|
|
4
|
-
from pydantic import
|
|
4
|
+
from pydantic import model_validator, BaseModel
|
|
5
5
|
|
|
6
6
|
from ul_api_utils.errors import SimpleValidateApiError
|
|
7
7
|
from ul_api_utils.utils.api_pagination import ApiPagination
|
|
@@ -42,7 +42,8 @@ class ApiRequestQuery(BaseModel):
|
|
|
42
42
|
offset: Optional[int] = None
|
|
43
43
|
page: Optional[int] = None
|
|
44
44
|
|
|
45
|
-
@
|
|
45
|
+
@model_validator(mode="before")
|
|
46
|
+
@classmethod
|
|
46
47
|
def validate_empty_values(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
|
47
48
|
vals = dict()
|
|
48
49
|
for k, v in values.items():
|
|
@@ -7,7 +7,7 @@ from tempfile import NamedTemporaryFile
|
|
|
7
7
|
from typing import List, Dict, Any, Optional, Tuple, Union, BinaryIO, Mapping, TypeVar, Callable, Set, TYPE_CHECKING
|
|
8
8
|
|
|
9
9
|
from flask import render_template, request
|
|
10
|
-
from
|
|
10
|
+
from pydantic_core import PydanticCustomError
|
|
11
11
|
from werkzeug.datastructures import FileStorage
|
|
12
12
|
|
|
13
13
|
from ul_api_utils.access import GLOBAL_PERMISSION__PUBLIC, PermissionDefinition, GLOBAL_PERMISSION__PRIVATE_RT, GLOBAL_PERMISSION__PRIVATE
|
|
@@ -170,12 +170,13 @@ class ApiResource:
|
|
|
170
170
|
return JsonApiResponse._internal_use_response_error(self._fn_typing.response_payload_many, 401, [self._mk_error("access-error", str(err), False)])
|
|
171
171
|
if isinstance(err, SimpleValidateApiError):
|
|
172
172
|
return JsonApiResponse._internal_use_response_error(self._fn_typing.response_payload_many, 400, [self._mk_error("validation-error", str(err), False)])
|
|
173
|
-
if isinstance(err, (
|
|
173
|
+
if isinstance(err, (PydanticCustomError, ValidateApiError)):
|
|
174
174
|
return JsonApiResponse._internal_use_response_error(self._fn_typing.response_payload_many, 400, [{
|
|
175
|
-
"error_kind": f"value_error.{err.code}",
|
|
175
|
+
"error_kind": f"value_error.{err.code}", # type: ignore
|
|
176
176
|
"error_location": err.location, # type: ignore
|
|
177
177
|
"error_type": "body-validation-error",
|
|
178
|
-
"error_message": f"{err.msg_template}",
|
|
178
|
+
"error_message": f"{err.msg_template}", # type: ignore
|
|
179
|
+
"error_input": err.input, # type: ignore
|
|
179
180
|
}])
|
|
180
181
|
if isinstance(err, NoResultFoundApiError):
|
|
181
182
|
return JsonApiResponse._internal_use_response_error(self._fn_typing.response_payload_many, 404, [
|
|
@@ -222,7 +223,7 @@ class ApiResource:
|
|
|
222
223
|
apply_auth_headers: bool = False,
|
|
223
224
|
) -> HtmlApiResponse:
|
|
224
225
|
if hasattr(happened_exception, "status_code"):
|
|
225
|
-
default_status_code = happened_exception.status_code
|
|
226
|
+
default_status_code = happened_exception.status_code
|
|
226
227
|
assert self._config.web_error_template is not None # only for mypy
|
|
227
228
|
response = HtmlApiResponse(
|
|
228
229
|
content=self.render_template(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import Optional, Callable, Tuple
|
|
2
2
|
|
|
3
|
-
from pydantic import
|
|
3
|
+
from pydantic import ConfigDict, BaseModel
|
|
4
4
|
|
|
5
5
|
from ul_api_utils.api_resource.api_response import ApiResponse
|
|
6
6
|
from werkzeug import Response as BaseResponse
|
|
@@ -14,6 +14,7 @@ class ApiResourceConfig(BaseModel):
|
|
|
14
14
|
exc_handler_endpoint: Optional[Callable[[Exception], Optional[ApiResponse]]] = None
|
|
15
15
|
override_flask_response: Optional[Callable[[Tuple[BaseResponse, int]], Tuple[BaseResponse, int]]] = None
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
extra
|
|
19
|
-
|
|
17
|
+
model_config = ConfigDict(
|
|
18
|
+
extra="forbid",
|
|
19
|
+
frozen=True,
|
|
20
|
+
)
|
|
@@ -2,8 +2,9 @@ import inspect
|
|
|
2
2
|
from typing import NamedTuple, Any, Callable, Optional, List, Dict, Type, Tuple, TYPE_CHECKING
|
|
3
3
|
|
|
4
4
|
from flask import request
|
|
5
|
-
from pydantic import BaseModel, ValidationError,
|
|
5
|
+
from pydantic import BaseModel, ValidationError, validate_call, TypeAdapter
|
|
6
6
|
from pydantic.utils import deep_update
|
|
7
|
+
from pydantic_core import ErrorDetails
|
|
7
8
|
|
|
8
9
|
from ul_api_utils.api_resource.api_request import ApiRequestQuery
|
|
9
10
|
from ul_api_utils.api_resource.api_resource_type import ApiResourceType
|
|
@@ -19,23 +20,23 @@ from ul_api_utils.utils.unwrap_typing import UnwrappedOptionalObjOrListOfObj
|
|
|
19
20
|
|
|
20
21
|
if TYPE_CHECKING:
|
|
21
22
|
from ul_api_utils.api_resource.api_resource import ApiResource
|
|
22
|
-
from pydantic.error_wrappers import ErrorDict
|
|
23
23
|
|
|
24
24
|
FN_SYSTEM_PROPS = {"api_resource", "query", "body", "return", "body_validation_error", "query_validation_error"}
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
def _patch_errors(dest_errors: List[Dict[str, str]], errors: List[
|
|
27
|
+
def _patch_errors(dest_errors: List[Dict[str, str]], errors: List[ErrorDetails], kind: str) -> List[Dict[str, str]]:
|
|
28
28
|
for error in errors:
|
|
29
29
|
dest_errors.append({
|
|
30
30
|
"error_type": kind,
|
|
31
31
|
"error_message": error['msg'],
|
|
32
32
|
"error_location": error["loc"], # type: ignore
|
|
33
33
|
"error_kind": error["type"],
|
|
34
|
+
"error_input": error["input"],
|
|
34
35
|
})
|
|
35
36
|
return dest_errors
|
|
36
37
|
|
|
37
38
|
|
|
38
|
-
@
|
|
39
|
+
@validate_call()
|
|
39
40
|
def _body_list(root: List[Dict[str, Any]]) -> List[Any]: # only for pydantic validation
|
|
40
41
|
return root
|
|
41
42
|
|
|
@@ -72,12 +73,12 @@ class ApiResourceFnTyping(NamedTuple):
|
|
|
72
73
|
if self.request_body_optional:
|
|
73
74
|
if self.request_body_many:
|
|
74
75
|
class BodyTypingOptList(BaseModel):
|
|
75
|
-
__root__: Optional[List[body_typing]] # type: ignore
|
|
76
|
+
__root__: Optional[List[body_typing]] = None # type: ignore
|
|
76
77
|
|
|
77
78
|
return BodyTypingOptList
|
|
78
79
|
|
|
79
80
|
class BodyTypingOpt(BaseModel):
|
|
80
|
-
__root__: Optional[body_typing] # type: ignore
|
|
81
|
+
__root__: Optional[body_typing] = None # type: ignore
|
|
81
82
|
return BodyTypingOpt
|
|
82
83
|
|
|
83
84
|
if self.request_body_many:
|
|
@@ -154,8 +155,7 @@ class ApiResourceFnTyping(NamedTuple):
|
|
|
154
155
|
return kwargs, errors
|
|
155
156
|
body = self._get_body()
|
|
156
157
|
try:
|
|
157
|
-
|
|
158
|
-
kwargs['body'] = parse_obj_as(expected_typing, body)
|
|
158
|
+
kwargs['body'] = TypeAdapter(Type[BaseModel | List[BaseModel]]).validate_python(body)
|
|
159
159
|
except ValidationError as ve:
|
|
160
160
|
if self.has_body_validation_error:
|
|
161
161
|
kwargs['body_validation_error'] = ve
|
|
@@ -167,9 +167,11 @@ class ApiResourceFnTyping(NamedTuple):
|
|
|
167
167
|
if self.query_typing is None:
|
|
168
168
|
return kwargs, errors
|
|
169
169
|
try:
|
|
170
|
+
is_complex_field = lambda field: field.annotation and not isinstance(field.annotation, (str, int, float, bool, type(None))) # noqa: E731
|
|
170
171
|
kwargs["query"] = set_model(self.query_typing, {
|
|
171
172
|
**request.args.to_dict(),
|
|
172
|
-
**{key: value for key, value in request.args.to_dict(flat=False).items()
|
|
173
|
+
**{key: value for key, value in request.args.to_dict(flat=False).items()
|
|
174
|
+
if key in self.query_typing.model_fields and is_complex_field(self.query_typing.model_fields[key])},
|
|
173
175
|
})
|
|
174
176
|
except ValidationError as ve:
|
|
175
177
|
if self.has_query_validation_error:
|
|
@@ -182,7 +184,7 @@ class ApiResourceFnTyping(NamedTuple):
|
|
|
182
184
|
loc_err = []
|
|
183
185
|
for name, type_ in self.signatures_typing:
|
|
184
186
|
try:
|
|
185
|
-
kwargs[name] =
|
|
187
|
+
kwargs[name] = TypeAdapter(type_).validate_python(kwargs.get(name))
|
|
186
188
|
except ValidationError as e:
|
|
187
189
|
err = e.errors()[0]
|
|
188
190
|
err["loc"] = [name] # type: ignore
|
|
@@ -246,7 +248,7 @@ class ApiResourceFnTyping(NamedTuple):
|
|
|
246
248
|
ret_payload_res = get_typing(return_payload_typing)
|
|
247
249
|
if len(ret_payload_res) > 1:
|
|
248
250
|
return_payload_typing = ret_payload_res[1]
|
|
249
|
-
if ret_payload_res[0] != list:
|
|
251
|
+
if ret_payload_res[0] != list: # noqa: E721
|
|
250
252
|
raise TypeError(f'{fn.__name__} :: invalid response payload type wrapper. only List is supported')
|
|
251
253
|
response_many = True
|
|
252
254
|
|
|
@@ -4,8 +4,8 @@ from types import NoneType
|
|
|
4
4
|
from typing import TypeVar, Generic, List, Optional, Dict, Any, Callable, Union, BinaryIO, Tuple, Type
|
|
5
5
|
import msgpack
|
|
6
6
|
from flask import jsonify, send_file, redirect, Response, request
|
|
7
|
-
from pydantic import
|
|
8
|
-
from pydantic
|
|
7
|
+
from pydantic import ConfigDict, RootModel
|
|
8
|
+
from pydantic import BaseModel
|
|
9
9
|
from werkzeug import Response as BaseResponse
|
|
10
10
|
|
|
11
11
|
from ul_api_utils.api_resource.db_types import TPayloadInputUnion
|
|
@@ -28,9 +28,10 @@ class ApiResponse(BaseModel):
|
|
|
28
28
|
status_code: int
|
|
29
29
|
headers: Dict[str, str] = {}
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
extra
|
|
33
|
-
arbitrary_types_allowed
|
|
31
|
+
model_config = ConfigDict(
|
|
32
|
+
extra="forbid",
|
|
33
|
+
arbitrary_types_allowed=True
|
|
34
|
+
)
|
|
34
35
|
|
|
35
36
|
@classmethod
|
|
36
37
|
def _internal_use__mk_schema(cls, inner_type: Optional[Type[BaseModel]]) -> Type[BaseModel]:
|
|
@@ -87,7 +88,7 @@ class HtmlApiResponse(ApiResponse):
|
|
|
87
88
|
|
|
88
89
|
class FileApiResponse(ApiResponse):
|
|
89
90
|
file_path: Union[str, BinaryIO, io.BytesIO]
|
|
90
|
-
mimetype: Optional[str] # it will be auto-detected by extension if mimetype==None
|
|
91
|
+
mimetype: Optional[str] = None # it will be auto-detected by extension if mimetype==None
|
|
91
92
|
as_attachment: bool = False
|
|
92
93
|
download_name: Optional[str] = None
|
|
93
94
|
attachment_filename: Optional[str] = None
|
|
@@ -139,8 +140,7 @@ class EmptyJsonApiResponse(ApiResponse):
|
|
|
139
140
|
|
|
140
141
|
|
|
141
142
|
class JsonApiResponsePayload(BaseModel):
|
|
142
|
-
|
|
143
|
-
extra = Extra.ignore
|
|
143
|
+
model_config = ConfigDict(extra="ignore")
|
|
144
144
|
|
|
145
145
|
|
|
146
146
|
TResultPayloadUnion = Union[None, Dict[str, Any], JsonApiResponsePayload, List[JsonApiResponsePayload], List[Dict[str, Any]]]
|
|
@@ -153,8 +153,8 @@ TPayloadTotalUnion = Union[
|
|
|
153
153
|
]
|
|
154
154
|
|
|
155
155
|
|
|
156
|
-
class DictJsonApiResponsePayload(
|
|
157
|
-
|
|
156
|
+
class DictJsonApiResponsePayload(RootModel[Dict[str, Any]]):
|
|
157
|
+
pass
|
|
158
158
|
|
|
159
159
|
|
|
160
160
|
TProxyPayload = TypeVar('TProxyPayload', bound=Union[JsonApiResponsePayload, List[JsonApiResponsePayload], None])
|
|
@@ -208,7 +208,7 @@ class AnyJsonApiResponse(ApiResponse):
|
|
|
208
208
|
def _internal_use__mk_schema(cls, inner_type: Optional[Type[BaseModel]]) -> Type[BaseModel]:
|
|
209
209
|
class _ResponseStd(BaseModel):
|
|
210
210
|
ok: bool
|
|
211
|
-
payload: Any
|
|
211
|
+
payload: Any = None
|
|
212
212
|
errors: List[Dict[str, Any]]
|
|
213
213
|
total_count: Optional[int] = None
|
|
214
214
|
count: Optional[int] = None
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Any, Dict, Optional, Union, Type, TypeVar, Tuple, TYPE_CHECKING
|
|
2
2
|
from typing import _GenericAlias # type: ignore
|
|
3
3
|
|
|
4
|
-
from pydantic import BaseModel
|
|
4
|
+
from pydantic import BaseModel, RootModel
|
|
5
5
|
|
|
6
6
|
from ul_api_utils.utils.json_encoder import to_dict
|
|
7
7
|
|
|
@@ -14,8 +14,8 @@ TPydanticModel = TypeVar('TPydanticModel', bound=BaseModel)
|
|
|
14
14
|
def set_model(model: Type[TPydanticModel], data: Union[Dict[str, Any], TPydanticModel]) -> TPydanticModel:
|
|
15
15
|
if isinstance(data, model):
|
|
16
16
|
return data
|
|
17
|
-
if
|
|
18
|
-
return model(
|
|
17
|
+
if issubclass(model, RootModel):
|
|
18
|
+
return model(data).root
|
|
19
19
|
assert isinstance(data, dict), f'data must be dict. "{type(data).__name__}" was given'
|
|
20
20
|
return model(**data)
|
|
21
21
|
|
|
@@ -26,13 +26,13 @@ def set_model_dictable(model: Type[TPydanticModel], data: 'TDictable') -> Option
|
|
|
26
26
|
res: Optional[Dict[str, Any]] = to_dict(data)
|
|
27
27
|
if res is None:
|
|
28
28
|
return None
|
|
29
|
-
if
|
|
30
|
-
return model(
|
|
29
|
+
if issubclass(model, RootModel):
|
|
30
|
+
return model(data).root
|
|
31
31
|
return model(**res)
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
def get_typing(t: Type[Any]) -> Tuple[Type[Any], ...]:
|
|
35
|
-
if type(t) == _GenericAlias:
|
|
35
|
+
if type(t) == _GenericAlias: # noqa: E721
|
|
36
36
|
src_t = t
|
|
37
37
|
return src_t.__origin__, *(it for it in src_t.__args__)
|
|
38
38
|
return t, # noqa: C818
|
|
@@ -27,11 +27,11 @@ class CmdEncKeys(Cmd):
|
|
|
27
27
|
name: str
|
|
28
28
|
services: List[str]
|
|
29
29
|
dir: str
|
|
30
|
-
permissions_module: Optional[str]
|
|
31
|
-
permissions_uri: Optional[str]
|
|
32
|
-
env: Optional[str]
|
|
33
|
-
org_id: Optional[UUID]
|
|
34
|
-
user_id: Optional[UUID]
|
|
30
|
+
permissions_module: Optional[str] = None
|
|
31
|
+
permissions_uri: Optional[str] = None
|
|
32
|
+
env: Optional[str] = None
|
|
33
|
+
org_id: Optional[UUID] = None
|
|
34
|
+
user_id: Optional[UUID] = None
|
|
35
35
|
|
|
36
36
|
@staticmethod
|
|
37
37
|
def add_parser_args(parser: argparse.ArgumentParser) -> None:
|
|
@@ -29,7 +29,7 @@ class CmdGenerateNewApiUser(Cmd):
|
|
|
29
29
|
uri_auth_api: str
|
|
30
30
|
internal_access_key: str
|
|
31
31
|
permissions_list: List[int]
|
|
32
|
-
permissions_uri: Optional[str]
|
|
32
|
+
permissions_uri: Optional[str] = None
|
|
33
33
|
api_user_name: str
|
|
34
34
|
api_user_note: str
|
|
35
35
|
api_user_date_exp: datetime
|
|
@@ -85,7 +85,7 @@ class CmdGenApiFunctionDocumentation(Cmd):
|
|
|
85
85
|
|
|
86
86
|
@staticmethod
|
|
87
87
|
def load_functions(directory: str) -> Dict[str, Callable[..., Any]]:
|
|
88
|
-
function_name_object__map = {}
|
|
88
|
+
function_name_object__map: dict[str, Callable[..., Any]] = {}
|
|
89
89
|
for root, _dirs, files in os.walk(directory):
|
|
90
90
|
for file in files:
|
|
91
91
|
py_postfix = '.py'
|
|
@@ -29,8 +29,8 @@ class CmdStart(Cmd):
|
|
|
29
29
|
max_requests_jitter: int
|
|
30
30
|
worker_class: str
|
|
31
31
|
freeze_gc: bool
|
|
32
|
-
statsd_endpoint: Optional[str]
|
|
33
|
-
statsd_prefix: Optional[str]
|
|
32
|
+
statsd_endpoint: Optional[str] = None
|
|
33
|
+
statsd_prefix: Optional[str] = None
|
|
34
34
|
|
|
35
35
|
@property
|
|
36
36
|
def app_rel_dir(self) -> str:
|
ul_api_utils/debug/debugger.py
CHANGED
ul_api_utils/errors.py
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
from typing import Dict, List
|
|
2
|
-
|
|
3
|
-
from pydantic import PydanticValueError
|
|
1
|
+
from typing import Dict, List, Any
|
|
4
2
|
|
|
5
3
|
from ul_api_utils.internal_api.internal_api_error import InternalApiResponseErrorObj
|
|
6
4
|
|
|
@@ -94,7 +92,7 @@ class ResponseStatusAbstractInternalApiError(ResponseAbstractInternalApiError):
|
|
|
94
92
|
def __init__(self, status_code: int, errors: List[InternalApiResponseErrorObj]) -> None:
|
|
95
93
|
assert isinstance(status_code, int), f'status_code must be int. "{type(status_code).__name__}" was given'
|
|
96
94
|
assert status_code >= 400
|
|
97
|
-
super(ResponseStatusAbstractInternalApiError, self).__init__(f'status code error :: {status_code} :: {[e.
|
|
95
|
+
super(ResponseStatusAbstractInternalApiError, self).__init__(f'status code error :: {status_code} :: {[e.model_dump() for e in errors]}')
|
|
98
96
|
self.status_code = status_code
|
|
99
97
|
self.errors = errors
|
|
100
98
|
|
|
@@ -137,10 +135,18 @@ class ValidationListApiError(UserAbstractApiError):
|
|
|
137
135
|
self.errors = errors
|
|
138
136
|
|
|
139
137
|
|
|
140
|
-
class ValidateApiError(
|
|
141
|
-
code =
|
|
142
|
-
|
|
143
|
-
|
|
138
|
+
class ValidateApiError(UserAbstractApiError):
|
|
139
|
+
def __init__(self, code: str, location: list[Any], msg_template: str, input: Any = None):
|
|
140
|
+
self.code = code
|
|
141
|
+
self.location = location
|
|
142
|
+
self.msg_template = msg_template
|
|
143
|
+
self.input = input
|
|
144
|
+
|
|
145
|
+
def __str__(self): # type: ignore
|
|
146
|
+
return (
|
|
147
|
+
f"{self.msg_template} (code={self.code}, location={self.location}, "
|
|
148
|
+
f"input={self.input})"
|
|
149
|
+
)
|
|
144
150
|
|
|
145
151
|
|
|
146
152
|
class SimpleValidateApiError(UserAbstractApiError):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import Optional, Any, Dict, List, Tuple, Union
|
|
2
2
|
|
|
3
|
-
from pydantic import
|
|
3
|
+
from pydantic import ConfigDict, BaseModel
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class InternalApiResponseErrorObj(BaseModel):
|
|
@@ -8,8 +8,10 @@ class InternalApiResponseErrorObj(BaseModel):
|
|
|
8
8
|
error_message: str
|
|
9
9
|
error_location: Optional[Union[List[str], str, Tuple[str, ...]]] = None
|
|
10
10
|
error_kind: Optional[str] = None
|
|
11
|
+
error_input: Optional[Any] = None
|
|
11
12
|
other: Optional[Dict[str, Any]] = None
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
extra
|
|
15
|
-
|
|
14
|
+
model_config = ConfigDict(
|
|
15
|
+
extra="ignore",
|
|
16
|
+
frozen=True,
|
|
17
|
+
)
|
ul_api_utils/modules/api_sdk.py
CHANGED
|
@@ -150,7 +150,7 @@ class ApiSdk:
|
|
|
150
150
|
|
|
151
151
|
if db_config is not None and type(db_config).__name__ == 'DbConfig':
|
|
152
152
|
from ul_db_utils.utils.waiting_for_postgres import waiting_for_postgres
|
|
153
|
-
from ul_db_utils.modules.postgres_modules.db import db #
|
|
153
|
+
from ul_db_utils.modules.postgres_modules.db import db # yes, db already defined, but can not use two conigs
|
|
154
154
|
db_config._init_from_sdk_with_flask(self)
|
|
155
155
|
waiting_for_postgres(db_config.uri)
|
|
156
156
|
self._db = db
|
|
@@ -490,7 +490,7 @@ class ApiSdk:
|
|
|
490
490
|
elif api_resource._type == ApiResourceType.FILE:
|
|
491
491
|
result = api_resource.response_api_error(e)
|
|
492
492
|
else:
|
|
493
|
-
result = api_resource.response_web_error(
|
|
493
|
+
result = api_resource.response_web_error( # type: ignore
|
|
494
494
|
ResourceRuntimeApiError(f'unsupported type "{api_resource._type.value}" of error response'),
|
|
495
495
|
)
|
|
496
496
|
res = result.to_flask_response(d), result.status_code
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
from typing import List, Optional, Callable, Union
|
|
3
3
|
|
|
4
|
-
from pydantic import
|
|
4
|
+
from pydantic import ConfigDict, BaseModel
|
|
5
5
|
|
|
6
6
|
from ul_api_utils.access import PermissionRegistry, PermissionDefinition
|
|
7
7
|
from ul_api_utils.modules.api_sdk_jwt import ApiSdkJwt
|
|
@@ -56,8 +56,8 @@ class ApiSdkConfig(BaseModel):
|
|
|
56
56
|
|
|
57
57
|
api_route_path_prefix: str = '/api'
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
extra
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
model_config = ConfigDict(
|
|
60
|
+
extra="forbid",
|
|
61
|
+
frozen=True,
|
|
62
|
+
arbitrary_types_allowed=True,
|
|
63
|
+
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from pydantic import
|
|
1
|
+
from pydantic import ConfigDict, BaseModel
|
|
2
2
|
|
|
3
3
|
from ul_api_utils.resources.socketio import SocketIOConfig
|
|
4
4
|
|
|
@@ -6,8 +6,8 @@ from ul_api_utils.resources.socketio import SocketIOConfig
|
|
|
6
6
|
class WorkerSdkConfig(BaseModel):
|
|
7
7
|
socket_config: SocketIOConfig | None = None
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
extra
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
model_config = ConfigDict(
|
|
10
|
+
extra="forbid",
|
|
11
|
+
frozen=True,
|
|
12
|
+
arbitrary_types_allowed=True,
|
|
13
|
+
)
|
|
@@ -3,7 +3,7 @@ import hashlib
|
|
|
3
3
|
import inspect
|
|
4
4
|
import itertools
|
|
5
5
|
import logging
|
|
6
|
-
from _hashlib import HASH
|
|
6
|
+
from _hashlib import HASH
|
|
7
7
|
from collections import defaultdict
|
|
8
8
|
from enum import Enum
|
|
9
9
|
from typing import Optional, Callable, Dict, Tuple, List, Union, Literal, cast, TypeVar, Any, Set, TypedDict
|
|
@@ -65,7 +65,7 @@ class ULCache(Cache):
|
|
|
65
65
|
|
|
66
66
|
@staticmethod
|
|
67
67
|
def _has_common_elements(seq: Set[Tuple[str, ...]]) -> bool:
|
|
68
|
-
return bool(functools.reduce(set.intersection, map(set, seq)))
|
|
68
|
+
return bool(functools.reduce(set.intersection, map(set, seq)))
|
|
69
69
|
|
|
70
70
|
@staticmethod
|
|
71
71
|
def _format_cache_key(parts: List[Any]) -> str:
|
|
@@ -77,8 +77,8 @@ class ULCache(Cache):
|
|
|
77
77
|
source_check: bool,
|
|
78
78
|
hash_method: Callable[[bytes], HASH],
|
|
79
79
|
) -> str:
|
|
80
|
-
cache_key_parts = [request.path]
|
|
81
|
-
cache_hash = None
|
|
80
|
+
cache_key_parts: List[str] = [request.path]
|
|
81
|
+
cache_hash: HASH | None = None
|
|
82
82
|
if request.args:
|
|
83
83
|
args_as_sorted_tuple = tuple(sorted(pair for pair in request.args.items(multi=True)))
|
|
84
84
|
|
|
@@ -93,8 +93,8 @@ class ULCache(Cache):
|
|
|
93
93
|
cache_hash.update(func_source_code.encode("utf-8"))
|
|
94
94
|
else:
|
|
95
95
|
cache_hash = hash_method(func_source_code.encode("utf-8"))
|
|
96
|
-
|
|
97
|
-
cache_key_parts.append(
|
|
96
|
+
cache_hash_str = str(cache_hash.hexdigest()) if cache_hash is not None else ''
|
|
97
|
+
cache_key_parts.append(cache_hash_str)
|
|
98
98
|
|
|
99
99
|
return self._format_cache_key(cache_key_parts)
|
|
100
100
|
|
|
@@ -78,7 +78,7 @@ def load_debugger_static_scripts(sdk: 'ApiSdk') -> None:
|
|
|
78
78
|
for req_url in reversed(body.requests_hierarchy):
|
|
79
79
|
for k, api in internal_api_registry.items():
|
|
80
80
|
if req_url.startswith(k):
|
|
81
|
-
api_body = body.
|
|
81
|
+
api_body = body.model_dump()
|
|
82
82
|
api_body['requests_hierarchy'] = []
|
|
83
83
|
api_result = api.request_post('/debugger-explain', v=ApiPathVersion.NO_VERSION, json=api_body).check().typed(DebuggerExplainResponse)
|
|
84
84
|
return api_resource.response_ok(api_result.payload)
|
|
@@ -2,7 +2,7 @@ import time
|
|
|
2
2
|
from enum import Enum, IntEnum
|
|
3
3
|
from typing import List, Callable, Any, Optional, Dict, TYPE_CHECKING, Tuple, NamedTuple, Set, Union
|
|
4
4
|
|
|
5
|
-
from pydantic import Field, BaseModel,
|
|
5
|
+
from pydantic import model_validator, ConfigDict, Field, BaseModel, PositiveInt, TypeAdapter
|
|
6
6
|
from sqlalchemy.sql import text
|
|
7
7
|
from unipipeline.errors import UniError
|
|
8
8
|
from unipipeline.modules.uni import Uni
|
|
@@ -64,7 +64,7 @@ class HealthCheckStepType(Enum):
|
|
|
64
64
|
executed_result.check()
|
|
65
65
|
except (Client4XXInternalApiError, Server5XXInternalApiError) as e:
|
|
66
66
|
error = e
|
|
67
|
-
payload =
|
|
67
|
+
payload = TypeAdapter(HealthCheckApiResponse).validate_python(executed_result.payload_raw).checks
|
|
68
68
|
if error:
|
|
69
69
|
if isinstance(error, Client4XXInternalApiError):
|
|
70
70
|
return HealthCheckResultStatus.WARN, '', payload
|
|
@@ -144,7 +144,7 @@ class HealthCheckStep(NamedTuple):
|
|
|
144
144
|
type: HealthCheckStepType
|
|
145
145
|
executable: Callable[..., Any]
|
|
146
146
|
executable_args: Tuple[Any, ...] = tuple()
|
|
147
|
-
executable_kwargs: Any = Field(default_factory=dict)
|
|
147
|
+
executable_kwargs: Any = Field(default_factory=lambda: dict())
|
|
148
148
|
|
|
149
149
|
def run(self) -> Tuple[HealthCheckResultStatus, float, str, List[HealthCheckResult]]:
|
|
150
150
|
start_time = time.perf_counter()
|
|
@@ -380,8 +380,7 @@ class HealthCheckApiResponse(JsonApiResponsePayload):
|
|
|
380
380
|
service_name: str
|
|
381
381
|
checks: List[HealthCheckResult] = Field(default_factory=list)
|
|
382
382
|
|
|
383
|
-
|
|
384
|
-
use_enum_values = True
|
|
383
|
+
model_config = ConfigDict(use_enum_values=True)
|
|
385
384
|
|
|
386
385
|
|
|
387
386
|
class HealthCheckMessageQueueRange(BaseModel):
|
|
@@ -407,13 +406,13 @@ class HealthCheckMessageQueueRange(BaseModel):
|
|
|
407
406
|
description="Maximum number of messages for queue to have in order to be classified with status WARN (400).",
|
|
408
407
|
)
|
|
409
408
|
|
|
410
|
-
@
|
|
411
|
-
def root_validate(
|
|
412
|
-
ok, warn =
|
|
409
|
+
@model_validator(mode='after')
|
|
410
|
+
def root_validate(self) -> 'HealthCheckMessageQueueRange':
|
|
411
|
+
ok, warn = self.ok, self.warn
|
|
413
412
|
if ok is not None and warn is not None:
|
|
414
413
|
if ok >= warn:
|
|
415
414
|
raise ValueError(f"OK attribute should be less than WARN attribute, but you provided {ok=}, {warn=}")
|
|
416
|
-
return
|
|
415
|
+
return self
|
|
417
416
|
|
|
418
417
|
def get_status(self, message_count: int) -> HealthCheckResultStatus:
|
|
419
418
|
if message_count <= self.ok:
|
|
@@ -437,5 +436,4 @@ class HealthCheckQueueMessageCountStatus(BaseModel):
|
|
|
437
436
|
queue_name: str
|
|
438
437
|
message_count_status_map: Dict[range, HealthCheckResultStatus]
|
|
439
438
|
|
|
440
|
-
|
|
441
|
-
arbitrary_types_allowed = True
|
|
439
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
@@ -17,7 +17,7 @@ class SwaggerModel(ABC):
|
|
|
17
17
|
def has_swagger_model_child_type(self, child_swagger_model_type) -> bool: # type: ignore
|
|
18
18
|
for swagger_model in self.swagger_models:
|
|
19
19
|
|
|
20
|
-
if
|
|
20
|
+
if isinstance(swagger_model, child_swagger_model_type):
|
|
21
21
|
return True
|
|
22
22
|
|
|
23
23
|
return False
|
|
@@ -27,7 +27,7 @@ class SwaggerModel(ABC):
|
|
|
27
27
|
|
|
28
28
|
for swagger_model in self.swagger_models:
|
|
29
29
|
|
|
30
|
-
if
|
|
30
|
+
if isinstance(swagger_model, swagger_model_type):
|
|
31
31
|
selection.append(swagger_model)
|
|
32
32
|
|
|
33
33
|
return selection
|
|
@@ -6,8 +6,7 @@ from datetime import datetime
|
|
|
6
6
|
from typing import Callable, List, Union, Any, Optional
|
|
7
7
|
|
|
8
8
|
from pydantic import BaseModel
|
|
9
|
-
from pydantic.fields import FieldInfo
|
|
10
|
-
from pydantic.main import ModelMetaclass
|
|
9
|
+
from pydantic.v1.fields import FieldInfo
|
|
11
10
|
|
|
12
11
|
from ul_api_utils.access import PermissionDefinition
|
|
13
12
|
from ul_api_utils.utils.flask_swagger_generator.exceptions import SwaggerGeneratorError
|
|
@@ -64,8 +63,8 @@ class SwaggerSchema(SwaggerModel):
|
|
|
64
63
|
self.items = {}
|
|
65
64
|
self.type = 'object'
|
|
66
65
|
|
|
67
|
-
if isinstance(schema,
|
|
68
|
-
json_schema = schema.
|
|
66
|
+
if isinstance(schema, type) and issubclass(schema, BaseModel):
|
|
67
|
+
json_schema = schema.model_json_schema()
|
|
69
68
|
if '$ref' in json.dumps(json_schema):
|
|
70
69
|
while '$ref' in json.dumps(json_schema):
|
|
71
70
|
json_schema = replace_value_in_dict(json_schema.copy(), json_schema.copy())
|
|
@@ -507,7 +506,7 @@ class SwaggerPath(SwaggerModel):
|
|
|
507
506
|
if not parameters:
|
|
508
507
|
parameters = SwaggerParameters() # type: ignore
|
|
509
508
|
swagger_request_type.add_swagger_model(parameters)
|
|
510
|
-
query_schema = query_model.
|
|
509
|
+
query_schema = query_model.model_json_schema()
|
|
511
510
|
query_required_fields = set(query_schema.get('required')) if query_schema.get('required') is not None else set()
|
|
512
511
|
query_definitions = query_schema.get('definitions')
|
|
513
512
|
for parameter_name, parameter_spec in query_schema.get('properties').items():
|
|
@@ -723,7 +722,7 @@ class SwaggerThreeSpecifier(SwaggerModel, SwaggerSpecifier):
|
|
|
723
722
|
self,
|
|
724
723
|
function_name: str,
|
|
725
724
|
status_code: int,
|
|
726
|
-
schema: Union[SwaggerSchema,
|
|
725
|
+
schema: Union[SwaggerSchema, type[BaseModel]],
|
|
727
726
|
description: str = "",
|
|
728
727
|
) -> None:
|
|
729
728
|
if not isinstance(schema, SwaggerSchema):
|
|
@@ -20,11 +20,11 @@ def to_dict(obj: 'TDictable') -> Optional[Dict[str, Any]]:
|
|
|
20
20
|
if isinstance(obj, dict):
|
|
21
21
|
return obj
|
|
22
22
|
if isinstance(obj, tuple) and hasattr(obj, '_asdict'): # NamedTuple
|
|
23
|
-
return obj._asdict()
|
|
23
|
+
return obj._asdict()
|
|
24
24
|
if dataclasses.is_dataclass(obj):
|
|
25
25
|
return dataclasses.asdict(obj)
|
|
26
26
|
if isinstance(obj, BaseModel):
|
|
27
|
-
return obj.
|
|
27
|
+
return obj.model_dump()
|
|
28
28
|
if has_already_imported_db():
|
|
29
29
|
from ul_db_utils.modules.postgres_modules.db import DbModel
|
|
30
30
|
from flask_sqlalchemy import BaseQuery, Model
|
|
@@ -57,7 +57,7 @@ class CustomJSONEncoder(JSONEncoder):
|
|
|
57
57
|
if isinstance(obj, (decimal.Decimal)):
|
|
58
58
|
return str(obj)
|
|
59
59
|
if isinstance(obj, BaseModel):
|
|
60
|
-
return obj.
|
|
60
|
+
return obj.model_dump()
|
|
61
61
|
if isinstance(obj, datetime):
|
|
62
62
|
return str(obj.isoformat())
|
|
63
63
|
if isinstance(obj, date):
|
|
@@ -71,9 +71,9 @@ class CustomJSONEncoder(JSONEncoder):
|
|
|
71
71
|
if isinstance(obj, frozendict):
|
|
72
72
|
return dict(obj)
|
|
73
73
|
if dataclasses.is_dataclass(obj):
|
|
74
|
-
return dataclasses.asdict(obj)
|
|
74
|
+
return dataclasses.asdict(obj) # type: ignore
|
|
75
75
|
if hasattr(obj, "__html__"): # it needs for Flask ?
|
|
76
|
-
return str(obj.__html__())
|
|
76
|
+
return str(obj.__html__())
|
|
77
77
|
|
|
78
78
|
if has_already_imported_db():
|
|
79
79
|
from ul_db_utils.model.base_model import BaseModel as DbBaseModel
|
|
@@ -5,7 +5,7 @@ import redis
|
|
|
5
5
|
import ormsgpack
|
|
6
6
|
import collections
|
|
7
7
|
|
|
8
|
-
from pydantic import ValidationError,
|
|
8
|
+
from pydantic import ValidationError, BaseModel, TypeAdapter
|
|
9
9
|
from typing import Any, Type, overload, Iterator, KeysView, cast
|
|
10
10
|
|
|
11
11
|
from ul_api_utils.utils.instance_checks import isinstance_namedtuple
|
|
@@ -14,7 +14,7 @@ from ul_api_utils.utils.memory_db.errors import CompositeKeyError, UnsupportedPa
|
|
|
14
14
|
CompositeKeyT = tuple[str, Type[BaseModel]]
|
|
15
15
|
AnyKeyT = str | CompositeKeyT
|
|
16
16
|
AnyT = Any
|
|
17
|
-
RedisClientT = redis.StrictRedis | redis.Redis
|
|
17
|
+
RedisClientT = redis.StrictRedis[Any] | redis.Redis[Any]
|
|
18
18
|
ExpiryT = int | timedelta | None
|
|
19
19
|
|
|
20
20
|
|
|
@@ -65,8 +65,8 @@ class BaseMemoryDbRepository(collections.abc.MutableMapping[str, AnyT]):
|
|
|
65
65
|
value = ormsgpack.unpackb(self._db[composite_key_name])
|
|
66
66
|
try:
|
|
67
67
|
if isinstance(value, list):
|
|
68
|
-
return
|
|
69
|
-
return
|
|
68
|
+
return TypeAdapter(list[_parse_as_type]).validate_python(value) # type: ignore
|
|
69
|
+
return TypeAdapter(_parse_as_type).validate_python(value)
|
|
70
70
|
except ValidationError:
|
|
71
71
|
raise UnsupportedParsingType(f"Could not parse the value of key '{composite_key_name}' with type {_parse_as_type}") from None
|
|
72
72
|
|
|
@@ -82,7 +82,7 @@ class UnwrappedOptionalObjOrListOfObj(Generic[TVal]):
|
|
|
82
82
|
if isinstance(unwrapped_t, TypingType):
|
|
83
83
|
value_type = unwrapped_t
|
|
84
84
|
elif isinstance(unwrapped_t, TypingGeneric) and len(unwrapped_t.args) == 1:
|
|
85
|
-
if unwrapped_t.origin.value != list:
|
|
85
|
+
if unwrapped_t.origin.value != list: # noqa: E721
|
|
86
86
|
return None
|
|
87
87
|
if not isinstance(unwrapped_t.args[0], TypingType):
|
|
88
88
|
return None # type: ignore
|
|
@@ -91,7 +91,7 @@ class UnwrappedOptionalObjOrListOfObj(Generic[TVal]):
|
|
|
91
91
|
elif isinstance(unwrapped_t, TypingOptional):
|
|
92
92
|
optional = True
|
|
93
93
|
if isinstance(unwrapped_t.value, TypingGeneric):
|
|
94
|
-
if unwrapped_t.value.origin.value != list:
|
|
94
|
+
if unwrapped_t.value.origin.value != list: # noqa: E721
|
|
95
95
|
return None
|
|
96
96
|
if len(unwrapped_t.value.args) != 1 or not isinstance(unwrapped_t.value.args[0], TypingType):
|
|
97
97
|
return None
|
|
@@ -1,73 +1,22 @@
|
|
|
1
1
|
import csv
|
|
2
|
-
import
|
|
2
|
+
from typing import TypeVar, Generic, List, Union, Generator, Callable, Annotated, Any
|
|
3
3
|
|
|
4
|
-
from
|
|
5
|
-
from
|
|
6
|
-
from pydantic import ValidationError, ConstrainedStr, ConstrainedInt, ConstrainedList
|
|
7
|
-
from pydantic.fields import ModelField
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class NotEmptyList(ConstrainedList):
|
|
11
|
-
"""Type that can be used in pydantic field, that will validate empty lists."""
|
|
12
|
-
min_items = 1
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class CronScheduleStr(ConstrainedStr):
|
|
16
|
-
"""Type that can be used in pydantic field, that will validate cron-formatted strings."""
|
|
17
|
-
regex = re.compile(CRON_EXPRESSION_VALIDATION_REGEX)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class WhiteSpaceStrippedStr(ConstrainedStr):
|
|
21
|
-
"""Type that can be used in pydantic field, that will strip whitespaces strings."""
|
|
22
|
-
strip_whitespace = True
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class UTCOffsetSeconds(ConstrainedInt):
|
|
26
|
-
"""Type that can be used for pydantic fields to represent the offset from UTC."""
|
|
27
|
-
ge = MIN_UTC_OFFSET_SECONDS
|
|
28
|
-
le = MAX_UTC_OFFSET_SECONDS
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class PgTypePasswordStr(ConstrainedStr):
|
|
32
|
-
"""Type that can be used for pydantic fields to represent password with length from 6 to 72 characters."""
|
|
33
|
-
min_length = 6
|
|
34
|
-
max_length = 72
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class PgTypeShortStr(ConstrainedStr):
|
|
38
|
-
"""Type that can be used for pydantic fields to represent short string with length from 0 to 255 characters."""
|
|
39
|
-
min_length = 0
|
|
40
|
-
max_length = 255
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class PgTypeLongStr(ConstrainedStr):
|
|
44
|
-
"""Type that can be used for pydantic fields to represent short string with length from 0 to 1000 characters."""
|
|
45
|
-
min_length = 0
|
|
46
|
-
max_length = 1000
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class PgTypeInt32(ConstrainedInt):
|
|
50
|
-
"""Type that can be used for pydantic fields to represent int32 number."""
|
|
51
|
-
ge = -2147483648
|
|
52
|
-
lt = 2147483648
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class PgTypePositiveInt32(ConstrainedInt):
|
|
56
|
-
"""Type that can be used for pydantic fields to represent positive int32 number."""
|
|
57
|
-
ge = 0
|
|
58
|
-
lt = 2147483648
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
class PgTypeInt16(ConstrainedInt):
|
|
62
|
-
"""Type that can be used for pydantic fields to represent int16 (SMALLINT) number."""
|
|
63
|
-
ge = -32768
|
|
64
|
-
lt = 32768
|
|
4
|
+
from pydantic import ValidationError, Field, StringConstraints
|
|
5
|
+
from pydantic.v1.fields import ModelField
|
|
65
6
|
|
|
7
|
+
from ul_api_utils.const import CRON_EXPRESSION_VALIDATION_REGEX, MIN_UTC_OFFSET_SECONDS, MAX_UTC_OFFSET_SECONDS
|
|
66
8
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
9
|
+
NotEmptyListAnnotation = Annotated[list[Any], Field(min_length=1)]
|
|
10
|
+
CronScheduleAnnotation = Annotated[str, StringConstraints(pattern=CRON_EXPRESSION_VALIDATION_REGEX)]
|
|
11
|
+
WhiteSpaceStrippedStrAnnotation = Annotated[str, StringConstraints(strip_whitespace=True)]
|
|
12
|
+
UTCOffsetSecondsAnnotation = Annotated[int, Field(ge=MIN_UTC_OFFSET_SECONDS, le=MAX_UTC_OFFSET_SECONDS)]
|
|
13
|
+
PgTypePasswordStrAnnotation = Annotated[str, StringConstraints(min_length=6, max_length=72)]
|
|
14
|
+
PgTypeShortStrAnnotation = Annotated[str, StringConstraints(min_length=0, max_length=255)]
|
|
15
|
+
PgTypeLongStrAnnotation = Annotated[str, StringConstraints(min_length=0, max_length=1000)]
|
|
16
|
+
PgTypeInt32Annotation = Annotated[int, Field(ge=-2147483648, le=2147483648)]
|
|
17
|
+
PgTypePositiveInt32Annotation = Annotated[int, Field(ge=0, le=2147483648)]
|
|
18
|
+
PgTypeInt16Annotation = Annotated[int, Field(ge=-32768, le=32768)]
|
|
19
|
+
PgTypePositiveInt16Annotation = Annotated[int, Field(ge=0, le=32768)]
|
|
71
20
|
|
|
72
21
|
|
|
73
22
|
QueryParamsSeparatedListValueType = TypeVar('QueryParamsSeparatedListValueType')
|
|
@@ -109,6 +58,6 @@ class QueryParamsSeparatedList(Generic[QueryParamsSeparatedListValueType]):
|
|
|
109
58
|
if error:
|
|
110
59
|
errors.append(error)
|
|
111
60
|
if errors:
|
|
112
|
-
raise ValidationError(errors, cls)
|
|
61
|
+
raise ValidationError(errors, cls)
|
|
113
62
|
# Validation passed without errors, modify string to a list and cast the right type for every element
|
|
114
63
|
return [list_item.type_(value) for value in splitted]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ul-api-utils
|
|
3
|
-
Version: 8.0.
|
|
3
|
+
Version: 8.0.3
|
|
4
4
|
Summary: Python api utils
|
|
5
5
|
Author: Unic-lab
|
|
6
6
|
Author-email:
|
|
@@ -17,13 +17,11 @@ Classifier: Operating System :: OS Independent
|
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
License-File: LICENSE
|
|
19
19
|
Requires-Dist: unipipeline (==1.9.4)
|
|
20
|
-
Requires-Dist:
|
|
21
|
-
Requires-Dist:
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist: flask (==
|
|
24
|
-
Requires-Dist: flask-
|
|
25
|
-
Requires-Dist: flask-limiter (==3.10.1)
|
|
26
|
-
Requires-Dist: flask-caching (==2.3.0)
|
|
20
|
+
Requires-Dist: jinja2 (==3.1.2)
|
|
21
|
+
Requires-Dist: flask (==2.1.3)
|
|
22
|
+
Requires-Dist: flask-wtf (==1.0.1)
|
|
23
|
+
Requires-Dist: flask-limiter (==2.5.1)
|
|
24
|
+
Requires-Dist: flask-caching (==2.1.0)
|
|
27
25
|
Requires-Dist: flask-swagger-ui (==4.11.1)
|
|
28
26
|
Requires-Dist: flask-monitoringdashboard (==3.1.2)
|
|
29
27
|
Requires-Dist: pycryptodome (==3.15.0)
|
|
@@ -46,7 +44,7 @@ Requires-Dist: faker (==24.8.0)
|
|
|
46
44
|
Requires-Dist: types-requests (==2.28.8)
|
|
47
45
|
Requires-Dist: types-jinja2 (==2.11.9)
|
|
48
46
|
Requires-Dist: xlsxwriter (==3.2.0)
|
|
49
|
-
Requires-Dist: werkzeug (==3.
|
|
47
|
+
Requires-Dist: werkzeug (==2.3.7)
|
|
50
48
|
Requires-Dist: frozendict (==2.4.4)
|
|
51
49
|
Requires-Dist: wtforms (==3.0.1)
|
|
52
50
|
Requires-Dist: wtforms-alchemy (==0.18.0)
|
|
@@ -7,7 +7,7 @@ example/rate_limit_load.py,sha256=U2Bgp8UztT4TNKdv9NVioxWfE68aCsC7uKz7xPCy6XM,22
|
|
|
7
7
|
example/redis_repository.py,sha256=ew9Ixdn0rAAE5iUINdxLJ9BdbhoD09FJzigdc7cNFAc,504
|
|
8
8
|
example/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
example/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
example/routes/api_some.py,sha256=
|
|
10
|
+
example/routes/api_some.py,sha256=tTv30Qtgo1wicCxmhbliQrk8Arq6qFwHVyjKfIopnUI,13044
|
|
11
11
|
example/sockets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
12
|
example/sockets/on_connect.py,sha256=3sluHnVT_-e2Oa4YsRZUJIc2L4VnMSJu5rgI_mkg8BQ,411
|
|
13
13
|
example/sockets/on_disconnect.py,sha256=ev0uJutqmTSsbrBcQIRudBX-6g7uSmZj8Imuf6pVIMU,277
|
|
@@ -19,29 +19,29 @@ example/workers/worker.py,sha256=Bb0JZPWJ8X51iCo6tlQ3umpqjS1kL3oy-FW9MtWH7aA,969
|
|
|
19
19
|
ul_api_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
20
|
ul_api_utils/conf.py,sha256=zLWsA0TTmekzxYrs5PbeqEkeylaNC_gIui3CGM9aZew,3487
|
|
21
21
|
ul_api_utils/const.py,sha256=pzY-zRznCJjZ0mFlte6XEsQQCU7EydN2WweEsVHSE7k,2563
|
|
22
|
-
ul_api_utils/errors.py,sha256=
|
|
22
|
+
ul_api_utils/errors.py,sha256=QQ3Zn7pRwr1q2JSogThAk4qIAveHif5_AN68vzK53bA,8404
|
|
23
23
|
ul_api_utils/main.py,sha256=-0S7qYgyNnom9xUhqowi0ErrYz82m3Z4K_uVxarXhN8,922
|
|
24
24
|
ul_api_utils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
25
|
ul_api_utils/sentry.py,sha256=UH_SwZCAoKH-Nw5B9CVQMoF-b1BJOp-ZTzwqUZ3Oq84,1801
|
|
26
|
-
ul_api_utils/access/__init__.py,sha256=
|
|
26
|
+
ul_api_utils/access/__init__.py,sha256=NUyRNvCVwfePrfdn5ATFVfHeSO3iq4-Syeup4IAZGzs,4526
|
|
27
27
|
ul_api_utils/api_resource/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
-
ul_api_utils/api_resource/api_request.py,sha256=
|
|
29
|
-
ul_api_utils/api_resource/api_resource.py,sha256=
|
|
30
|
-
ul_api_utils/api_resource/api_resource_config.py,sha256=
|
|
28
|
+
ul_api_utils/api_resource/api_request.py,sha256=6Ag2trKIkenhYYPU2--hnfNJC5lLgBxVQ7jXj5D1eXM,3303
|
|
29
|
+
ul_api_utils/api_resource/api_resource.py,sha256=j-E8KJiXWS1L0oVIerJZlbGpDL2ijlQrck4GrJPWPyE,17840
|
|
30
|
+
ul_api_utils/api_resource/api_resource_config.py,sha256=l9OYJy75UZLshOkEQDO5jlhXeb5H4HDPu-nLOjuoexw,769
|
|
31
31
|
ul_api_utils/api_resource/api_resource_error_handling.py,sha256=E0SWpjFSIP-4SumbgzrHtFuFiGe9q38WsvLROt0YcPE,1168
|
|
32
|
-
ul_api_utils/api_resource/api_resource_fn_typing.py,sha256=
|
|
32
|
+
ul_api_utils/api_resource/api_resource_fn_typing.py,sha256=mKaeGzkOJ-FyDM6B55isUnr8sFsgSHeILQkxJd_tpI8,18343
|
|
33
33
|
ul_api_utils/api_resource/api_resource_type.py,sha256=mgjSQI3swGpgpLI6y35LYtFrdN-kXyV5cQorwGW7h6g,462
|
|
34
|
-
ul_api_utils/api_resource/api_response.py,sha256=
|
|
34
|
+
ul_api_utils/api_resource/api_response.py,sha256=l3sQ34DkybP8nQWW8DSQulihfrB0Ai0RBaCrVZAHeSk,9695
|
|
35
35
|
ul_api_utils/api_resource/api_response_db.py,sha256=ucY6ANPlHZml7JAbvq-PL85z0bvERTjEJKvz-REPyok,888
|
|
36
36
|
ul_api_utils/api_resource/api_response_payload_alias.py,sha256=53dYTvKS2mCIgE3DvGcMUrpFIM1007s_d1mslj0gLDk,559
|
|
37
37
|
ul_api_utils/api_resource/db_types.py,sha256=LFw7mnzY4e6WuEYkUzPSgs6b-aw2vnRSqYsJMEMWUhA,436
|
|
38
|
-
ul_api_utils/api_resource/signature_check.py,sha256=
|
|
38
|
+
ul_api_utils/api_resource/signature_check.py,sha256=jahhr7ttYEUKen_Wp2Eh1__oxqu7ZDoYgw0diK9CFzc,1266
|
|
39
39
|
ul_api_utils/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
|
-
ul_api_utils/commands/cmd_enc_keys.py,sha256
|
|
40
|
+
ul_api_utils/commands/cmd_enc_keys.py,sha256=HvwihTrLMqhQO_nYA8U0oowqrTKsmUGzseRofuSCO9k,8488
|
|
41
41
|
ul_api_utils/commands/cmd_gen_api_user_token.py,sha256=Vg7oEYHvof7DSLat9yJ_k5AYL9ZOC4Jvd38DBn5U-R0,2730
|
|
42
|
-
ul_api_utils/commands/cmd_gen_new_api_user.py,sha256=
|
|
43
|
-
ul_api_utils/commands/cmd_generate_api_docs.py,sha256=
|
|
44
|
-
ul_api_utils/commands/cmd_start.py,sha256=
|
|
42
|
+
ul_api_utils/commands/cmd_gen_new_api_user.py,sha256=ungpVG_ecAl0hHcVdcKQ4TrJ5Iv0hLAkD_GRH8innqk,4556
|
|
43
|
+
ul_api_utils/commands/cmd_generate_api_docs.py,sha256=LIAyBMyZD7oQRmuDrrqYBj8XHEpRKJy82ybuE6EXVZE,9643
|
|
44
|
+
ul_api_utils/commands/cmd_start.py,sha256=Q0sKa4Uq73ZrKiAlfa4ZdraAWT1v47i5wDyjs8xSBzA,4731
|
|
45
45
|
ul_api_utils/commands/cmd_worker_start.py,sha256=1tt4_mL8T8_q7i1bqnfjPSkSYlRNNNp8eJ-5rTYj36w,2593
|
|
46
46
|
ul_api_utils/commands/start/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
47
47
|
ul_api_utils/commands/start/gunicorn.conf.local.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -50,7 +50,7 @@ ul_api_utils/commands/start/wsgi.py,sha256=ZBFx66XP8iNliK3vkB6yWRkCq2-ItgieweBmW
|
|
|
50
50
|
ul_api_utils/conf/ul-debugger-main.js,sha256=XgzaH1AWAG4_PW9mRaoJGJOKeEA5T61j7yIouZjU65s,999747
|
|
51
51
|
ul_api_utils/conf/ul-debugger-ui.js,sha256=bNwv6ntu8RjCrH33H5eTUtFXdBoMrgFt3P87ujbBmRU,2043
|
|
52
52
|
ul_api_utils/debug/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
|
-
ul_api_utils/debug/debugger.py,sha256=
|
|
53
|
+
ul_api_utils/debug/debugger.py,sha256=eBsYJjBjvUT2yfr8YuJSNSOETVlbz8jAvTPvIKTUvoY,3716
|
|
54
54
|
ul_api_utils/debug/malloc.py,sha256=OvESxpn8sQMyAb64DxnYUAofRZdnJ1I199IUBWiIoa4,3274
|
|
55
55
|
ul_api_utils/debug/stat.py,sha256=udlLLIopxj6SmHH8Scqz5fuNa_CucMlRx7qp88Fi5pY,14591
|
|
56
56
|
ul_api_utils/encrypt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -59,24 +59,24 @@ ul_api_utils/encrypt/encrypt_decrypt_aes_xtea.py,sha256=Gj-X_CoYY2PPrczTcG9Ho_dg
|
|
|
59
59
|
ul_api_utils/internal_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
60
60
|
ul_api_utils/internal_api/internal_api.py,sha256=gLlz4TYDgrmEF8S2XpNCKgAjLHsd21vs75kppHLoG6A,14067
|
|
61
61
|
ul_api_utils/internal_api/internal_api_check_context.py,sha256=FJyYuyhp7AzvKSoyNrszw6vtUanSUKNNcDmlNH-UDI4,1709
|
|
62
|
-
ul_api_utils/internal_api/internal_api_error.py,sha256=
|
|
62
|
+
ul_api_utils/internal_api/internal_api_error.py,sha256=sdm3V2VLSfFVBmxaeo2Wy2wkhmxWTXGsCCR-u08ChMg,471
|
|
63
63
|
ul_api_utils/internal_api/internal_api_response.py,sha256=rNAqY82ezupcRSnWY1YO2T5QhwfOFrak0dp23shgUxY,11583
|
|
64
64
|
ul_api_utils/internal_api/__tests__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
65
65
|
ul_api_utils/internal_api/__tests__/internal_api.py,sha256=X2iopeso6vryszeeA__lcqXQVtz3Nwt3ngH7M4OuN1U,1116
|
|
66
66
|
ul_api_utils/internal_api/__tests__/internal_api_content_type.py,sha256=mfiYPkzKtfZKFpi4RSnWAoCd6mRijr6sFsa2TF-s5t8,749
|
|
67
67
|
ul_api_utils/modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
68
|
-
ul_api_utils/modules/api_sdk.py,sha256=
|
|
69
|
-
ul_api_utils/modules/api_sdk_config.py,sha256=
|
|
68
|
+
ul_api_utils/modules/api_sdk.py,sha256=IKcQYnx22GHV2paH4qvJcCqsM5LXpUVIMfrl6bevTRE,26506
|
|
69
|
+
ul_api_utils/modules/api_sdk_config.py,sha256=ZUR48tIJeFlPJTSjyXzKfXaCKPtfqeaA0mlLX42SSFY,2137
|
|
70
70
|
ul_api_utils/modules/api_sdk_jwt.py,sha256=2XRfb0LxHUnldSL67S60v1uyoDpVPNaq4zofUtkeg88,15112
|
|
71
71
|
ul_api_utils/modules/intermediate_state.py,sha256=7ZZ3Sypbb8LaSfrVhaXaWRDnj8oyy26NUbmFK7vr-y4,1270
|
|
72
72
|
ul_api_utils/modules/worker_context.py,sha256=jGjopeuYuTtIDmsrqK7TcbTD-E81t8OWvWS1JpTC6b0,802
|
|
73
73
|
ul_api_utils/modules/worker_sdk.py,sha256=4jObKYGAvZNrH0HWl3Wi7TXkNtlbC5PjkkrMLwDU17s,5144
|
|
74
|
-
ul_api_utils/modules/worker_sdk_config.py,sha256=
|
|
74
|
+
ul_api_utils/modules/worker_sdk_config.py,sha256=7y-J1yHtoHQuRZyye--pY85yaJXKL6Vh9hODQHMESyU,308
|
|
75
75
|
ul_api_utils/modules/__tests__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
76
76
|
ul_api_utils/modules/__tests__/test_api_sdk_jwt.py,sha256=9JJTtva2z4pjvTWQqo_0EOvzf4wBgvq0G77jM0SC3Bg,10719
|
|
77
77
|
ul_api_utils/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
78
|
-
ul_api_utils/resources/caching.py,sha256=
|
|
79
|
-
ul_api_utils/resources/debugger_scripts.py,sha256=
|
|
78
|
+
ul_api_utils/resources/caching.py,sha256=gSMZp8C1GfTZeCOrlC56yBILkvBql5umEuOBg0_y3ec,7511
|
|
79
|
+
ul_api_utils/resources/debugger_scripts.py,sha256=QWFDHrBJuptnl-XI-dbNdqKH5AoyylSvfIBwRHso6Sw,5100
|
|
80
80
|
ul_api_utils/resources/not_implemented.py,sha256=OQE5LGA4KqZDwP5Wtub3Aw-icwzbqCSKcEFoFp4w7_k,973
|
|
81
81
|
ul_api_utils/resources/permissions.py,sha256=8c8cEPkm69zxgXbDiwUkW6Mi_496-MZXbPOxHITetKs,1436
|
|
82
82
|
ul_api_utils/resources/rate_limitter.py,sha256=QMpaLu1LoFbvtIEhxF8HbAZIAW4oGKTClt7XEUsqx98,3358
|
|
@@ -84,7 +84,7 @@ ul_api_utils/resources/socketio.py,sha256=oeQdULtBnslyMJkeqfZMWxtK6efLFX1LrfA2cB
|
|
|
84
84
|
ul_api_utils/resources/swagger.py,sha256=fK8S9X4YCSqe_weCzV_BcMPoL_NR073BsGUzn2ImbHI,5391
|
|
85
85
|
ul_api_utils/resources/health_check/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
86
86
|
ul_api_utils/resources/health_check/const.py,sha256=QzVZP_ZKmVKleUACiOGjzP-v54FD7tEN6NEF4Jb50Ow,78
|
|
87
|
-
ul_api_utils/resources/health_check/health_check.py,sha256=
|
|
87
|
+
ul_api_utils/resources/health_check/health_check.py,sha256=rfbvwfsWaQgj-DP_En_-wPEYZdPq2K4-5wz6rjZG0oU,18478
|
|
88
88
|
ul_api_utils/resources/health_check/health_check_template.py,sha256=Qih-sVoFVoVxfmDYBTzwlNSicCr7zNelUJLJMnM-C_Q,2572
|
|
89
89
|
ul_api_utils/resources/health_check/resource.py,sha256=SPd9kMzBOVhFZgMVfV26bDpZya3BmwxTfOR4MZ2dL4o,4199
|
|
90
90
|
ul_api_utils/resources/web_forms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -111,11 +111,11 @@ ul_api_utils/utils/deprecated.py,sha256=xR3ELgoDj7vJEY4CAYeEhdbtSJTfkukbjxcULtpM
|
|
|
111
111
|
ul_api_utils/utils/flags.py,sha256=AYN5nKWp4-uu6PSlPptL7ZiLqr3Pu-x5dffF6SBsqfg,957
|
|
112
112
|
ul_api_utils/utils/imports.py,sha256=i8PhoD0c_jnWTeXt_VxW_FihynwXSL_dHRT7jQiFyXE,376
|
|
113
113
|
ul_api_utils/utils/instance_checks.py,sha256=9punTfY5uabuJhmSGLfTgBqRderoFysCXBSI8rvbPco,467
|
|
114
|
-
ul_api_utils/utils/json_encoder.py,sha256=
|
|
114
|
+
ul_api_utils/utils/json_encoder.py,sha256=ra43WhkAadIvVpUY3uik_Nzg0LVXUXTGYvx4Gx7fD_g,4578
|
|
115
115
|
ul_api_utils/utils/load_modules.py,sha256=_CPmQuB6o_33FE6zFl_GyO5xS5gmjfNffB6k-cglKAA,685
|
|
116
116
|
ul_api_utils/utils/token_check.py,sha256=-Quuh8gOs9fNE1shYhdiMpQedafsLN7MB2ilSxG_F8E,489
|
|
117
117
|
ul_api_utils/utils/token_check_through_request.py,sha256=OyyObu6Btk9br7auIYvWcMULhNznNSD5T0mWOwZX7Uk,663
|
|
118
|
-
ul_api_utils/utils/unwrap_typing.py,sha256=
|
|
118
|
+
ul_api_utils/utils/unwrap_typing.py,sha256=19jyhkRcb89v1gzR62YDsMO9zxR2PU5bjYZm1is8Aoo,4189
|
|
119
119
|
ul_api_utils/utils/uuid_converter.py,sha256=OZMuySkoALrQQOe312_BHVWN20Sz5frKuH9KYziAGsU,565
|
|
120
120
|
ul_api_utils/utils/__tests__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
121
121
|
ul_api_utils/utils/__tests__/api_path_version.py,sha256=n7VFVdwFqP_FS6PE3OUCS68oU3tG78xM4HxrKShLhNw,898
|
|
@@ -124,9 +124,9 @@ ul_api_utils/utils/flask_swagger_generator/__init__.py,sha256=47DEQpj8HBSa-_TImW
|
|
|
124
124
|
ul_api_utils/utils/flask_swagger_generator/conf.py,sha256=ngnNoJUfBj14ICIH8TJnDakO9i2C5Ukb8x6n5oIgQws,110
|
|
125
125
|
ul_api_utils/utils/flask_swagger_generator/exceptions.py,sha256=yA4IsUyxh5puyoYzuM8dyrXUVhWje66rrr2j2qMNtXU,168
|
|
126
126
|
ul_api_utils/utils/flask_swagger_generator/specifiers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
127
|
-
ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_models.py,sha256=
|
|
127
|
+
ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_models.py,sha256=P52azB25Ncpp6I5j5bq4LLAJHDLSORFVJp9bHoXvpQk,1698
|
|
128
128
|
ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_specifier.py,sha256=9Cf1ijk90IDYUDG8kTjK4cxjdxpYjWZz1tKJHPCeDAA,1513
|
|
129
|
-
ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_three_specifier.py,sha256=
|
|
129
|
+
ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_three_specifier.py,sha256=4zkH3P41DwPXsriJISc7aL0IYjKR7yKfoKAzf9arQW0,30837
|
|
130
130
|
ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_version.py,sha256=A14IRG-e2KL2SlFbHep2FH0uMRIHPhfd7KLkYdtWrfA,1312
|
|
131
131
|
ul_api_utils/utils/flask_swagger_generator/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
132
132
|
ul_api_utils/utils/flask_swagger_generator/utils/input_type.py,sha256=Ynp3zI5q1F0Tl_eTdNbWoCxRKPwBCJkwJOoeHE2pTRE,2533
|
|
@@ -140,17 +140,17 @@ ul_api_utils/utils/jinja/t_url_for.py,sha256=PG9W4UbkWv2pLXNMQiCt22vp4sDi-Uz5w2u
|
|
|
140
140
|
ul_api_utils/utils/jinja/to_pretty_json.py,sha256=wcc_EJ6yM4lipE0Vr6cgYOB-rBk7_j_Wa7bijjI_bCs,302
|
|
141
141
|
ul_api_utils/utils/memory_db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
142
142
|
ul_api_utils/utils/memory_db/errors.py,sha256=Bw1O1Y_WTCeCRNgb9iwrGT1fst6iHORhrNstwxiaUu8,267
|
|
143
|
-
ul_api_utils/utils/memory_db/repository.py,sha256=
|
|
143
|
+
ul_api_utils/utils/memory_db/repository.py,sha256=xOnxEJyApGTglqjQ5fPKcEV5rHEkvKwcgrUfW4zJbHg,3754
|
|
144
144
|
ul_api_utils/utils/memory_db/__tests__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
145
145
|
ul_api_utils/validators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
146
|
-
ul_api_utils/validators/custom_fields.py,sha256=
|
|
146
|
+
ul_api_utils/validators/custom_fields.py,sha256=zjusHZ4mOCAmn5MSRGnHkKkexw4r7x0vt5e4dQJctgg,3001
|
|
147
147
|
ul_api_utils/validators/validate_empty_object.py,sha256=3Ck_iwyJE_M5e7l6s1i88aqb73zIt06uaLrMG2PAb0A,299
|
|
148
148
|
ul_api_utils/validators/validate_uuid.py,sha256=EfvlRirv2EW0Z6w3s8E8rUa9GaI8qXZkBWhnPs8NFrA,257
|
|
149
149
|
ul_api_utils/validators/__tests__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
150
150
|
ul_api_utils/validators/__tests__/test_custom_fields.py,sha256=QLZ7DFta01Z7DOK9Z5Iq4uf_CmvDkVReis-GAl_QN48,1447
|
|
151
|
-
ul_api_utils-8.0.
|
|
152
|
-
ul_api_utils-8.0.
|
|
153
|
-
ul_api_utils-8.0.
|
|
154
|
-
ul_api_utils-8.0.
|
|
155
|
-
ul_api_utils-8.0.
|
|
156
|
-
ul_api_utils-8.0.
|
|
151
|
+
ul_api_utils-8.0.3.dist-info/LICENSE,sha256=6Qo8OdcqI8aGrswJKJYhST-bYqxVQBQ3ujKdTSdq-80,1062
|
|
152
|
+
ul_api_utils-8.0.3.dist-info/METADATA,sha256=gz25UoxRnq-cCOyZQ5qBxrwc8-OQ0MY2RsXr9xE3dYM,14744
|
|
153
|
+
ul_api_utils-8.0.3.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
|
154
|
+
ul_api_utils-8.0.3.dist-info/entry_points.txt,sha256=8tL3ySHWTyJMuV1hx1fHfN8zumDVOCOm63w3StphkXg,53
|
|
155
|
+
ul_api_utils-8.0.3.dist-info/top_level.txt,sha256=1XsW8iOSFaH4LOzDcnNyxHpHrbKU3fSn-aIAxe04jmw,21
|
|
156
|
+
ul_api_utils-8.0.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|