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.

Files changed (33) hide show
  1. example/routes/api_some.py +1 -1
  2. ul_api_utils/access/__init__.py +2 -3
  3. ul_api_utils/api_resource/api_request.py +3 -2
  4. ul_api_utils/api_resource/api_resource.py +6 -5
  5. ul_api_utils/api_resource/api_resource_config.py +5 -4
  6. ul_api_utils/api_resource/api_resource_fn_typing.py +13 -11
  7. ul_api_utils/api_resource/api_response.py +11 -11
  8. ul_api_utils/api_resource/signature_check.py +6 -6
  9. ul_api_utils/commands/cmd_enc_keys.py +5 -5
  10. ul_api_utils/commands/cmd_gen_new_api_user.py +1 -1
  11. ul_api_utils/commands/cmd_generate_api_docs.py +1 -1
  12. ul_api_utils/commands/cmd_start.py +2 -2
  13. ul_api_utils/debug/debugger.py +1 -1
  14. ul_api_utils/errors.py +14 -8
  15. ul_api_utils/internal_api/internal_api_error.py +6 -4
  16. ul_api_utils/modules/api_sdk.py +2 -2
  17. ul_api_utils/modules/api_sdk_config.py +6 -6
  18. ul_api_utils/modules/worker_sdk_config.py +6 -6
  19. ul_api_utils/resources/caching.py +6 -6
  20. ul_api_utils/resources/debugger_scripts.py +1 -1
  21. ul_api_utils/resources/health_check/health_check.py +9 -11
  22. ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_models.py +2 -2
  23. ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_three_specifier.py +5 -6
  24. ul_api_utils/utils/json_encoder.py +5 -5
  25. ul_api_utils/utils/memory_db/repository.py +4 -4
  26. ul_api_utils/utils/unwrap_typing.py +2 -2
  27. ul_api_utils/validators/custom_fields.py +16 -67
  28. {ul_api_utils-8.0.1a0.dist-info → ul_api_utils-8.0.3.dist-info}/METADATA +7 -9
  29. {ul_api_utils-8.0.1a0.dist-info → ul_api_utils-8.0.3.dist-info}/RECORD +33 -33
  30. {ul_api_utils-8.0.1a0.dist-info → ul_api_utils-8.0.3.dist-info}/LICENSE +0 -0
  31. {ul_api_utils-8.0.1a0.dist-info → ul_api_utils-8.0.3.dist-info}/WHEEL +0 -0
  32. {ul_api_utils-8.0.1a0.dist-info → ul_api_utils-8.0.3.dist-info}/entry_points.txt +0 -0
  33. {ul_api_utils-8.0.1a0.dist-info → ul_api_utils-8.0.3.dist-info}/top_level.txt +0 -0
@@ -51,7 +51,7 @@ class Eeenum(IntEnum):
51
51
 
52
52
  class SomeBody(BaseModel):
53
53
  seconds: float
54
- eenum: Optional[Eeenum]
54
+ eenum: Optional[Eeenum] = None
55
55
 
56
56
 
57
57
  class Some2Query(ApiRequestQuery):
@@ -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
- class Config:
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 BaseModel, root_validator
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
- @root_validator(pre=True)
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 pydantic.errors import PydanticValueError
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, (PydanticValueError, ValidateApiError)):
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 # type: ignore
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 BaseModel, Extra
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
- class Config:
18
- extra = Extra.forbid
19
- allow_mutation = False
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, parse_obj_as, validate_arguments
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['ErrorDict'], kind: str) -> List[Dict[str, str]]:
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
- @validate_arguments
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
- expected_typing: Type[BaseModel | List[BaseModel]] = List[self.body_typing] if self.request_body_many else self.body_typing # type: ignore
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() if key in self.query_typing.__fields__ and self.query_typing.__fields__[key].is_complex()},
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] = parse_obj_as(type_, kwargs.get(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 Extra
8
- from pydantic.main import BaseModel
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
- class Config:
32
- extra = Extra.forbid
33
- arbitrary_types_allowed = True
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
- class Config:
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(JsonApiResponsePayload):
157
- __root__: Dict[str, Any]
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 "__root__" in model.__fields__:
18
- return model(__root__=data).__root__ # type: ignore
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 "__root__" in model.__fields__:
30
- return model(__root__=res).__root__ # type: ignore
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:
@@ -22,7 +22,7 @@ class DebuggerJSONEncoder(JSONEncoder):
22
22
  if isinstance(obj, set):
23
23
  return list(obj)
24
24
  if hasattr(obj, "__html__"): # it needs for Flask ?
25
- return str(obj.__html__()) # type: ignore
25
+ return str(obj.__html__())
26
26
  return super().default(obj)
27
27
 
28
28
 
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.dict() for e in errors]}')
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(PydanticValueError, UserAbstractApiError):
141
- code = "{code}"
142
- location = ["{location}"]
143
- msg_template = "{msg_template}"
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 BaseModel, Extra
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
- class Config:
14
- extra = Extra.ignore
15
- allow_mutation = False
14
+ model_config = ConfigDict(
15
+ extra="ignore",
16
+ frozen=True,
17
+ )
@@ -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 # type: ignore # yes, db already defined, but can not use two conigs
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 BaseModel, Extra
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
- class Config:
60
- extra = Extra.forbid
61
- allow_mutation = False
62
- frozen = True
63
- arbitrary_types_allowed = True
59
+ model_config = ConfigDict(
60
+ extra="forbid",
61
+ frozen=True,
62
+ arbitrary_types_allowed=True,
63
+ )
@@ -1,4 +1,4 @@
1
- from pydantic import BaseModel, Extra
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
- class Config:
10
- extra = Extra.forbid
11
- allow_mutation = False
12
- frozen = True
13
- arbitrary_types_allowed = True
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 # type: ignore
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))) # type: ignore
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
- cache_hash = str(cache_hash.hexdigest()) if cache_hash is not None else ''
97
- cache_key_parts.append(cache_hash)
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.dict()
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, parse_obj_as, PositiveInt, root_validator
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 = parse_obj_as(HealthCheckApiResponse, executed_result.payload_raw).checks
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
- class Config:
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
- @root_validator()
411
- def root_validate(cls, values: Dict[str, Any]) -> Dict[str, Any]:
412
- ok, warn = values.get('ok'), values.get('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 values
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
- class Config:
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 type(swagger_model) == child_swagger_model_type:
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 type(swagger_model) == swagger_model_type:
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, ModelMetaclass):
68
- json_schema = 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.schema()
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, ModelMetaclass],
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() # type: ignore
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.dict()
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.dict()
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__()) # type: ignore
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, parse_obj_as, BaseModel
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 parse_obj_as(list[_parse_as_type], value) # type: ignore
69
- return parse_obj_as(_parse_as_type, value)
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 re
2
+ from typing import TypeVar, Generic, List, Union, Generator, Callable, Annotated, Any
3
3
 
4
- from ul_api_utils.const import CRON_EXPRESSION_VALIDATION_REGEX, MIN_UTC_OFFSET_SECONDS, MAX_UTC_OFFSET_SECONDS
5
- from typing import TypeVar, Generic, List, Union, Generator, Callable
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
- class PgTypePositiveInt16(ConstrainedInt):
68
- """Type that can be used for pydantic fields to represent positive int16 (SMALLINT) number."""
69
- ge = 0
70
- lt = 32768
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) # type: ignore
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.1a0
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: pydantic[mypy] (==2.10.6)
21
- Requires-Dist: pydantic-i18n (==0.2.3)
22
- Requires-Dist: jinja2 (==3.1.5)
23
- Requires-Dist: flask (==3.1.0)
24
- Requires-Dist: flask-wtf (==1.2.2)
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.1.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=rjguCsuDH8XpuM1gUcIe2dSbUnACR0sh-z6zQHBH0cM,13037
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=kmmgNXJtgVczOskVG8Ye1WSHbqSSAWC2OzUjPUGCkGo,8137
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=OD2UnvAi8ax40B_5JlaW2IAcOcZzk2Y15h8Xi_wEa0g,4526
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=psVfLJqapV4PBL3YF0Bj22T3PDcCWQME87chtGBH3pw,3279
29
- ul_api_utils/api_resource/api_resource.py,sha256=mUaFWxYBfHemcM2SaBQBOtTb4T4fbvZ4CNmE2GwPtOg,17766
30
- ul_api_utils/api_resource/api_resource_config.py,sha256=_B7DNe5eLbRw0Spy1y5CatXjlz5M3wqgYIjnJemlY44,760
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=OrmVFSuvqER5FwlhA6N6iaO3N99iPfTapBv_X_6xcbo,18224
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=jb5mKjouupDmctzPb2b7Z9_dpWVuxUHxVSevz-8sODI,9676
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=CpUsV460P5f7gN4nyQtKoRsgm4ZqoqICbVxsM2xzhnc,1302
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=-Tblh6lI7G6M5YVwbVQqZmXhBMiIpB3a7b0Lv1MFufk,8453
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=ICZbKqz2D6DRvjwtNM08rNjIlWN3qClcUQw5L8FxRBY,4549
43
- ul_api_utils/commands/cmd_generate_api_docs.py,sha256=pafX88RjcSMFruSk6qy8Xfao3MXo1yolI4rkC_bmMSQ,9612
44
- ul_api_utils/commands/cmd_start.py,sha256=cd1PLHaB4LXNHwZBP1NyllHHSetuI-XUDZw2MGlbkWE,4717
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=1FovBJSPDZ79W6f0tpf8tZLqXM73elcWGdQ96b6k18E,3732
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=Y5PcKXIONSJZRc0XzvHwFtgrKML78Y3sdvX3vXRMmnQ,424
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=AkakVkObpaJ0ldlhCtQIJTEQ9Ze0okBebdV5evTiu7o,26505
69
- ul_api_utils/modules/api_sdk_config.py,sha256=3r46W5jNKlx7K2MWP94QuM216fmQkhP6LtCRZ9KmNEs,2151
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=SAMKoY0AWE-As1afDCjsvVQCpU7ouRBwf0X298NoNMo,322
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=bboV5XGxW5hJ1NnKz92AsrF9jw8C80odxXsAO9y33IA,7516
79
- ul_api_utils/resources/debugger_scripts.py,sha256=qqf6zeJ1aE_STNYX1j9-ZfBgB_jcQoOaPX10-X3nPE8,5094
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=bb_ONn3akNXxNFELIUcW6foqwiKqeinmr-Wl0fQ7Q4c,18449
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=3ReY76A79Wg5yjNam8qTKyHAMb9uxyM1EV9Jberwcqo,4582
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=fmkAv8bbyQrq1GWFpUlkdF5vPI3aw4ASv_fiYqpQtkY,4161
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=eMRDP60GUbIuBGinFPfjp7VsnbgmzockTsIT-KadchA,1690
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=ReG74_qMT8yVLJAIDZBwcWRxWgACaEgmfxzc0p5dmyY,30828
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=FDauZgcYiQWn-yYGCMsvaT4C2CES8WG2iSwcpVmmXu4,3715
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=iqDnAvO-Bs13ZtLtccAG8SdJ8jWDlVcW6_ckChrTdXQ,4023
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.1a0.dist-info/LICENSE,sha256=6Qo8OdcqI8aGrswJKJYhST-bYqxVQBQ3ujKdTSdq-80,1062
152
- ul_api_utils-8.0.1a0.dist-info/METADATA,sha256=Qy2x9Cf3XuZkLP9GpgI8ZgR_oqJQZ-g4p6e7I_Qgw78,14827
153
- ul_api_utils-8.0.1a0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
154
- ul_api_utils-8.0.1a0.dist-info/entry_points.txt,sha256=8tL3ySHWTyJMuV1hx1fHfN8zumDVOCOm63w3StphkXg,53
155
- ul_api_utils-8.0.1a0.dist-info/top_level.txt,sha256=1XsW8iOSFaH4LOzDcnNyxHpHrbKU3fSn-aIAxe04jmw,21
156
- ul_api_utils-8.0.1a0.dist-info/RECORD,,
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,,