u-toolkit 0.1.11__tar.gz → 0.1.13__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/PKG-INFO +1 -1
  2. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/pyproject.toml +2 -1
  3. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/decorators.py +8 -4
  4. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/fastapi/cbv.py +132 -123
  5. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/tests/fastapi/test_cbv.py +4 -2
  6. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/uv.lock +37 -1
  7. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/.github/workflows/publish.yml +0 -0
  8. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/.gitignore +0 -0
  9. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/README.md +0 -0
  10. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/__init__.py +0 -0
  11. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/alias_generators.py +0 -0
  12. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/datetime.py +0 -0
  13. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/enum.py +0 -0
  14. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/fastapi/__init__.py +0 -0
  15. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/fastapi/config.py +0 -0
  16. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/fastapi/exception.py +0 -0
  17. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/fastapi/helpers.py +0 -0
  18. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/fastapi/lifespan.py +0 -0
  19. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/fastapi/pagination.py +0 -0
  20. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/fastapi/responses.py +0 -0
  21. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/function.py +0 -0
  22. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/helpers.py +0 -0
  23. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/logger.py +0 -0
  24. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/merge.py +0 -0
  25. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/object.py +0 -0
  26. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/path.py +0 -0
  27. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/pydantic/__init__.py +0 -0
  28. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/pydantic/fields.py +0 -0
  29. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/pydantic/models.py +0 -0
  30. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/pydantic/type_vars.py +0 -0
  31. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/signature.py +0 -0
  32. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/sqlalchemy/__init__.py +0 -0
  33. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/sqlalchemy/fields.py +0 -0
  34. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/sqlalchemy/function.py +0 -0
  35. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/sqlalchemy/orm/__init__.py +0 -0
  36. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/sqlalchemy/orm/fields.py +0 -0
  37. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/sqlalchemy/orm/models.py +0 -0
  38. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/sqlalchemy/table_info.py +0 -0
  39. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/src/u_toolkit/sqlalchemy/type_vars.py +0 -0
  40. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/tests/__init__.py +0 -0
  41. {u_toolkit-0.1.11 → u_toolkit-0.1.13}/tests/fastapi/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: u-toolkit
3
- Version: 0.1.11
3
+ Version: 0.1.13
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: pydantic>=2.11.3
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "u-toolkit"
3
- version = "0.1.11"
3
+ version = "0.1.13"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -30,6 +30,7 @@ dev = [
30
30
  "httpx>=0.28.1",
31
31
  "pytest>=8.3.5",
32
32
  "python-multipart>=0.0.20",
33
+ "rich>=14.0.0",
33
34
  "ruff>=0.11.6",
34
35
  "uvicorn>=0.34.2",
35
36
  ]
@@ -1,9 +1,8 @@
1
+ import inspect
1
2
  from collections.abc import Callable
2
3
  from functools import wraps
3
4
  from typing import Generic, NamedTuple, TypeVar
4
5
 
5
- from u_toolkit.signature import list_parameters, update_parameters
6
-
7
6
 
8
7
  _FnT = TypeVar("_FnT", bound=Callable)
9
8
 
@@ -27,8 +26,13 @@ class DefineMethodDecorator(Generic[_T, _FnT]):
27
26
  self.register_method(DefineMethodParams(owner_class, name, self.fn))
28
27
 
29
28
  def __get__(self, instance: _T, owner_class: type[_T]):
30
- parameters = list_parameters(self.fn)[1:]
31
- update_parameters(self.fn, *parameters)
29
+ if inspect.iscoroutinefunction(self.fn):
30
+
31
+ @wraps(self.fn)
32
+ async def awrapper(*args, **kwargs):
33
+ return await self.fn(instance, *args, **kwargs)
34
+
35
+ return awrapper
32
36
 
33
37
  @wraps(self.fn)
34
38
  def wrapper(*args, **kwargs):
@@ -3,7 +3,16 @@ import re
3
3
  from collections.abc import Callable
4
4
  from enum import Enum, StrEnum, auto
5
5
  from functools import partial, update_wrapper, wraps
6
- from typing import Any, Literal, NamedTuple, Protocol, Self, TypeVar, cast
6
+ from typing import (
7
+ Generic,
8
+ Literal,
9
+ NamedTuple,
10
+ NotRequired,
11
+ TypeVar,
12
+ TypedDict,
13
+ cast,
14
+ overload,
15
+ )
7
16
 
8
17
  from fastapi import APIRouter, Depends
9
18
  from pydantic.alias_generators import to_snake
@@ -20,23 +29,7 @@ from u_toolkit.signature import (
20
29
  )
21
30
 
22
31
 
23
- class EndpointsClassInterface(Protocol):
24
- dependencies: tuple | None = None
25
- responses: tuple[Response, ...] | None = None
26
- prefix: str | None = None
27
- tags: tuple[str | Enum, ...] | None = None
28
- deprecated: bool | None = None
29
-
30
- @classmethod
31
- def build_self(cls) -> Self:
32
- return cls()
33
-
34
-
35
32
  _T = TypeVar("_T")
36
- EndpointsClassInterfaceT = TypeVar(
37
- "EndpointsClassInterfaceT",
38
- bound=EndpointsClassInterface,
39
- )
40
33
 
41
34
 
42
35
  LiteralUpperMethod = Literal[
@@ -84,15 +77,6 @@ METHOD_PATTERNS = {
84
77
  _FnName = str
85
78
 
86
79
 
87
- class EndpointInfo(NamedTuple):
88
- handle: Callable
89
- original_handle_name: str
90
- handle_name: str
91
- method: RequestMethod
92
- method_pattern: re.Pattern
93
- path: str
94
-
95
-
96
80
  _MethodInfo = tuple[RequestMethod, re.Pattern[str]]
97
81
 
98
82
 
@@ -103,9 +87,13 @@ def get_method(name: str) -> _MethodInfo | None:
103
87
  return None
104
88
 
105
89
 
106
- def valid_endpoint(name: str):
107
- if get_method(name) is None:
108
- raise ValueError("Invalid endpoint function.")
90
+ class EndpointInfo(NamedTuple):
91
+ handle: Callable
92
+ original_handle_name: str
93
+ handle_name: str
94
+ method: RequestMethod
95
+ method_pattern: re.Pattern
96
+ path: str
109
97
 
110
98
 
111
99
  def iter_endpoints(
@@ -125,13 +113,19 @@ def iter_endpoints(
125
113
  paths = [prefix]
126
114
 
127
115
  methods: list[_MethodInfo] = []
128
- if method := get_method(name):
116
+ from_valid_method = False
117
+
118
+ if valid_method:
119
+ if results := valid_method(name, handle):
120
+ methods.extend(results)
121
+ if methods:
122
+ from_valid_method = True
123
+
124
+ if not methods and (method := get_method(name)):
129
125
  methods.append(method)
130
- elif valid_method:
131
- methods.extend(valid_method(name, handle) or [])
132
126
 
133
127
  for method, pattern in methods:
134
- handle_name = pattern.sub("", name)
128
+ handle_name = name if from_valid_method else pattern.sub("", name)
135
129
  path = handle_name.replace("__", "/")
136
130
  if path:
137
131
  paths.append(path)
@@ -161,52 +155,53 @@ def iter_dependencies(cls: type[_T]):
161
155
  yield token, dep
162
156
 
163
157
 
164
- _CBVEndpointParamName = Literal[
165
- "path",
166
- "tags",
167
- "dependencies",
168
- "responses",
169
- "response_model",
170
- "status",
171
- "deprecated",
172
- "methods",
173
- ]
158
+ class CBVRoutesInfo(TypedDict):
159
+ path: NotRequired[str | None]
160
+ tags: NotRequired[list[str | Enum] | None]
161
+ dependencies: NotRequired[list | None]
162
+ responses: NotRequired[list[Response] | None]
163
+ deprecated: NotRequired[bool | None]
164
+
165
+
166
+ class CBVRouteInfo(CBVRoutesInfo, Generic[_T]):
167
+ methods: NotRequired[list[RequestMethod] | None]
168
+ response_model: NotRequired[type[_T] | None]
169
+ status: NotRequired[int | None]
174
170
 
175
171
 
176
172
  class CBV:
177
173
  def __init__(self, router: APIRouter | None = None) -> None:
178
174
  self.router = router or APIRouter()
179
175
 
180
- self._state: dict[
181
- type[EndpointsClassInterface],
182
- dict[_FnName, dict[_CBVEndpointParamName, Any]],
183
- ] = {}
184
-
185
- self._initialed_state: dict[
186
- type[EndpointsClassInterface], EndpointsClassInterface
176
+ self._state: dict[type, dict[_FnName, CBVRouteInfo]] = {}
177
+ self._cls_routes_extra: dict[
178
+ type, tuple[CBVRoutesInfo, Callable[[type[_T]], _T] | None] # type: ignore
187
179
  ] = {}
180
+ self._initialed_state: dict[type[_T], _T] = {} # type: ignore
188
181
 
189
182
  def create_route(
190
183
  self,
191
184
  *,
192
- cls: type[EndpointsClassInterfaceT],
185
+ cls: type[_T],
193
186
  path: str,
194
187
  method: RequestMethod,
195
188
  method_name: str,
196
189
  ):
197
- class_tags = list(cls.tags) if cls.tags else []
190
+ class_routes_info = self._cls_routes_extra[cls][0]
191
+
192
+ class_tags = class_routes_info.get("tags") or []
198
193
  endpoint_tags: list[str | Enum] = (
199
194
  self._state[cls][method_name].get("tags") or []
200
195
  )
201
196
  tags = class_tags + endpoint_tags
202
197
 
203
- class_dependencies = list(cls.dependencies) if cls.dependencies else []
198
+ class_dependencies = class_routes_info.get("dependencies") or []
204
199
  endpoint_dependencies = (
205
200
  self._state[cls][method_name].get("dependencies") or []
206
201
  )
207
202
  dependencies = class_dependencies + endpoint_dependencies
208
203
 
209
- class_responses = cls.responses or []
204
+ class_responses = class_routes_info.get("responses") or []
210
205
  endpoint_responses = (
211
206
  self._state[cls][method_name].get("responses") or []
212
207
  )
@@ -215,13 +210,14 @@ class CBV:
215
210
  status_code = self._state[cls][method_name].get("status")
216
211
 
217
212
  deprecated = self._state[cls][method_name].get(
218
- "deprecated", cls.deprecated
213
+ "deprecated", class_routes_info.get("deprecated")
219
214
  )
220
215
 
221
216
  response_model = self._state[cls][method_name].get("response_model")
222
217
 
223
- endpoint_methods = self._state[cls][method_name].get("methods") or [
224
- method
218
+ endpoint_methods = [
219
+ i.upper()
220
+ for i in (self._state[cls][method_name].get("methods") or [method])
225
221
  ]
226
222
 
227
223
  path = self._state[cls][method_name].get("path") or path
@@ -245,22 +241,22 @@ class CBV:
245
241
  tags: list[str | Enum] | None = None,
246
242
  dependencies: list | None = None,
247
243
  responses: list[Response] | None = None,
248
- response_model: Any | None = None,
244
+ response_model: type[_T] | None = None,
249
245
  status: int | None = None,
250
246
  deprecated: bool | None = None,
251
247
  ):
252
248
  state = self._state
253
249
  initial_state = self._initial_state
254
- data: dict[_CBVEndpointParamName, Any] = {
255
- "path": path,
256
- "methods": methods,
257
- "tags": tags,
258
- "dependencies": dependencies,
259
- "responses": responses,
260
- "response_model": response_model,
261
- "status": status,
262
- "deprecated": deprecated,
263
- }
250
+ data = CBVRouteInfo(
251
+ path=path,
252
+ methods=methods,
253
+ tags=tags,
254
+ dependencies=dependencies,
255
+ responses=responses,
256
+ response_model=response_model,
257
+ status=status,
258
+ deprecated=deprecated,
259
+ )
264
260
 
265
261
  def handle(params: DefineMethodParams):
266
262
  initial_state(params.method_class)
@@ -271,37 +267,27 @@ class CBV:
271
267
 
272
268
  return define_method_handler(handle)
273
269
 
274
- def _initial_state(self, cls: type[_T]) -> EndpointsClassInterface:
275
- if result := self._initialed_state.get(cls): # type: ignore
276
- return result
277
-
278
- self._update_cls(cls)
279
- n_cls = cast(type[EndpointsClassInterface], cls)
270
+ def _initial_state(self, cls: type[_T]) -> _T:
271
+ if result := self._initialed_state.get(cls):
272
+ return cast(_T, result)
280
273
 
281
274
  default_data = {}
282
- for endpoint in iter_endpoints(n_cls):
275
+ for endpoint in iter_endpoints(cls):
283
276
  default_data[endpoint.original_handle_name] = {}
284
277
 
285
- self._state.setdefault(n_cls, default_data)
286
- result = self._build_cls(n_cls)
287
- self._initialed_state[n_cls] = result
278
+ self._state.setdefault(cls, default_data)
279
+ result = self._build_cls(cls)
280
+ self._initialed_state[cls] = result
288
281
  return result
289
282
 
290
- def _update_cls(self, cls: type[_T]):
291
- for extra_name in EndpointsClassInterface.__annotations__:
292
- if not hasattr(cls, extra_name):
293
- setattr(cls, extra_name, None)
294
-
295
- # TODO: 加个如果存在属性, 校验属性类型是否是预期的
296
-
297
283
  def _build_cls(self, cls: type[_T]) -> _T:
298
- if inspect.isfunction(cls.__init__) and hasattr(cls, "build_self"):
299
- return cast(type[EndpointsClassInterface], cls).build_self() # type: ignore
284
+ if cls in self._cls_routes_extra and (
285
+ build := self._cls_routes_extra[cls][1]
286
+ ):
287
+ return build(cls) # type: ignore
300
288
  return cls()
301
289
 
302
- def __create_class_dependencies_injector(
303
- self, cls: type[EndpointsClassInterfaceT]
304
- ):
290
+ def __create_class_dependencies_injector(self, cls: type[_T]):
305
291
  """将类的依赖添加到函数实例上
306
292
 
307
293
  ```python
@@ -333,7 +319,7 @@ class CBV:
333
319
 
334
320
  def new_fn(method_name, kwargs):
335
321
  instance = self._build_cls(cls)
336
- dependencies = kwargs.pop(collect_cls_dependencies.__name__)
322
+ dependencies = kwargs.pop(collect_cls_dependencies.__name__, {})
337
323
  for dep_name, dep_value in dependencies.items():
338
324
  setattr(instance, dep_name, dep_value)
339
325
  return getattr(instance, method_name)
@@ -374,38 +360,61 @@ class CBV:
374
360
 
375
361
  return decorator
376
362
 
377
- def __call__(self, cls: type[_T]) -> type[_T]:
378
- instance = self._initial_state(cls)
379
- cls_ = cast(type[EndpointsClassInterface], cls)
363
+ @overload
364
+ def __call__(self, cls: type[_T], /) -> type[_T]: ...
365
+ @overload
366
+ def __call__(
367
+ self,
368
+ *,
369
+ info: CBVRoutesInfo | None = None,
370
+ build: Callable[[type[_T]], _T] | None = None,
371
+ ) -> Callable[[type[_T]], type[_T]]: ...
372
+
373
+ def __call__(self, *args, **kwargs):
374
+ info: CBVRoutesInfo = {}
375
+ build: Callable | None = None
376
+
377
+ def decorator(cls: type[_T]) -> type[_T]:
378
+ instance = self._initial_state(cls)
379
+ self._cls_routes_extra[cls] = info, build
380
+
381
+ decorator = self.__create_class_dependencies_injector(cls)
382
+
383
+ def valid_method(
384
+ name: str, _handle: Callable
385
+ ) -> None | list[_MethodInfo]:
386
+ if (cls_state := self._state.get(cls)) and (
387
+ method_state := cls_state.get(name)
388
+ ):
389
+ methods: list[RequestMethod] = (
390
+ method_state.get("methods") or []
391
+ )
392
+ result: list[_MethodInfo] = []
393
+ for i in methods:
394
+ method = Methods(i.lower())
395
+ result.append((method, METHOD_PATTERNS[method]))
396
+ return result
397
+
398
+ return None
399
+
400
+ for endpoint_info in iter_endpoints(cls, valid_method):
401
+ route = self.create_route(
402
+ cls=cls,
403
+ path=endpoint_info.path,
404
+ method=endpoint_info.method,
405
+ method_name=endpoint_info.original_handle_name,
406
+ )
407
+ method = getattr(instance, endpoint_info.original_handle_name)
408
+ endpoint = decorator(method)
409
+ endpoint.__name__ = endpoint_info.handle_name
410
+ route(endpoint)
380
411
 
381
- decorator = self.__create_class_dependencies_injector(cls_)
412
+ return cls
382
413
 
383
- def valid_method(
384
- name: str, _handle: Callable
385
- ) -> None | list[_MethodInfo]:
386
- if (cls_state := self._state.get(cls_)) and (
387
- method_state := cls_state.get(name)
388
- ):
389
- methods: list[RequestMethod] = (
390
- method_state.get("methods") or []
391
- )
392
- result: list[_MethodInfo] = []
393
- for i in methods:
394
- method = Methods(i.lower())
395
- result.append((method, METHOD_PATTERNS[method]))
396
- return result
397
-
398
- return None
399
-
400
- for endpoint_info in iter_endpoints(cls, valid_method):
401
- route = self.create_route(
402
- cls=cast(type[EndpointsClassInterface], cls),
403
- path=endpoint_info.path,
404
- method=endpoint_info.method,
405
- method_name=endpoint_info.original_handle_name,
406
- )
407
- method = getattr(instance, endpoint_info.original_handle_name)
408
- endpoint = decorator(method)
409
- route(endpoint)
414
+ if args:
415
+ return decorator(args[0])
410
416
 
411
- return cls
417
+ info.update(kwargs.get("info") or {})
418
+ build = kwargs.get("build")
419
+
420
+ return decorator
@@ -83,7 +83,8 @@ class _NoPath:
83
83
  return data.username
84
84
 
85
85
  @cbv.info(methods=["DELETE"])
86
- async def wtf(self):
86
+ async def post_wtf(self):
87
+ await asyncio.sleep(1)
87
88
  return 1
88
89
 
89
90
 
@@ -107,8 +108,9 @@ def test_cbv():
107
108
  value = "example"
108
109
  assert (
109
110
  client.post(
110
- "/",
111
+ "/alala",
111
112
  data={"username": value, "password": value},
112
113
  ).json()
113
114
  == value
114
115
  )
116
+ assert client.delete("/post_wtf").json() == 1
@@ -202,6 +202,27 @@ wheels = [
202
202
  { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 },
203
203
  ]
204
204
 
205
+ [[package]]
206
+ name = "markdown-it-py"
207
+ version = "3.0.0"
208
+ source = { registry = "https://pypi.org/simple" }
209
+ dependencies = [
210
+ { name = "mdurl" },
211
+ ]
212
+ sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
213
+ wheels = [
214
+ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
215
+ ]
216
+
217
+ [[package]]
218
+ name = "mdurl"
219
+ version = "0.1.2"
220
+ source = { registry = "https://pypi.org/simple" }
221
+ sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
222
+ wheels = [
223
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
224
+ ]
225
+
205
226
  [[package]]
206
227
  name = "packaging"
207
228
  version = "25.0"
@@ -356,6 +377,19 @@ wheels = [
356
377
  { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 },
357
378
  ]
358
379
 
380
+ [[package]]
381
+ name = "rich"
382
+ version = "14.0.0"
383
+ source = { registry = "https://pypi.org/simple" }
384
+ dependencies = [
385
+ { name = "markdown-it-py" },
386
+ { name = "pygments" },
387
+ ]
388
+ sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078 }
389
+ wheels = [
390
+ { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229 },
391
+ ]
392
+
359
393
  [[package]]
360
394
  name = "ruff"
361
395
  version = "0.11.6"
@@ -471,7 +505,7 @@ wheels = [
471
505
 
472
506
  [[package]]
473
507
  name = "u-toolkit"
474
- version = "0.1.1"
508
+ version = "0.1.12"
475
509
  source = { editable = "." }
476
510
  dependencies = [
477
511
  { name = "pydantic" },
@@ -492,6 +526,7 @@ dev = [
492
526
  { name = "httpx" },
493
527
  { name = "pytest" },
494
528
  { name = "python-multipart" },
529
+ { name = "rich" },
495
530
  { name = "ruff" },
496
531
  { name = "uvicorn" },
497
532
  ]
@@ -511,6 +546,7 @@ dev = [
511
546
  { name = "httpx", specifier = ">=0.28.1" },
512
547
  { name = "pytest", specifier = ">=8.3.5" },
513
548
  { name = "python-multipart", specifier = ">=0.0.20" },
549
+ { name = "rich", specifier = ">=14.0.0" },
514
550
  { name = "ruff", specifier = ">=0.11.6" },
515
551
  { name = "uvicorn", specifier = ">=0.34.2" },
516
552
  ]
File without changes
File without changes
File without changes