u-toolkit 0.1.1__tar.gz → 0.1.2__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.1 → u_toolkit-0.1.2}/PKG-INFO +2 -1
  2. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/pyproject.toml +4 -4
  3. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/decorators.py +2 -1
  4. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/fastapi/cbv.py +11 -6
  5. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/signature.py +3 -4
  6. u_toolkit-0.1.2/tests/__init__.py +0 -0
  7. u_toolkit-0.1.2/tests/fastapi/__init__.py +0 -0
  8. u_toolkit-0.1.2/tests/fastapi/test_cbv.py +94 -0
  9. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/.github/workflows/publish.yml +0 -0
  10. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/.gitignore +0 -0
  11. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/README.md +0 -0
  12. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/__init__.py +0 -0
  13. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/alias_generators.py +0 -0
  14. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/datetime.py +0 -0
  15. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/enum.py +0 -0
  16. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/fastapi/__init__.py +0 -0
  17. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/fastapi/config.py +0 -0
  18. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/fastapi/exception.py +0 -0
  19. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/fastapi/helpers.py +0 -0
  20. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/fastapi/lifespan.py +0 -0
  21. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/fastapi/pagination.py +0 -0
  22. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/fastapi/responses.py +0 -0
  23. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/function.py +0 -0
  24. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/helpers.py +0 -0
  25. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/logger.py +0 -0
  26. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/merge.py +0 -0
  27. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/object.py +0 -0
  28. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/path.py +0 -0
  29. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/pydantic/__init__.py +0 -0
  30. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/pydantic/fields.py +0 -0
  31. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/pydantic/models.py +0 -0
  32. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/pydantic/type_vars.py +0 -0
  33. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/sqlalchemy/__init__.py +0 -0
  34. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/sqlalchemy/fields.py +0 -0
  35. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/sqlalchemy/function.py +0 -0
  36. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/sqlalchemy/orm/__init__.py +0 -0
  37. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/sqlalchemy/orm/fields.py +0 -0
  38. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/sqlalchemy/orm/models.py +0 -0
  39. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/sqlalchemy/table_info.py +0 -0
  40. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/src/u_toolkit/sqlalchemy/type_vars.py +0 -0
  41. {u_toolkit-0.1.1 → u_toolkit-0.1.2}/uv.lock +0 -0
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: u-toolkit
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: pydantic>=2.11.3
7
7
  Provides-Extra: fastapi
8
+ Requires-Dist: fastapi>=0.115.12; extra == 'fastapi'
8
9
  Requires-Dist: pydantic-settings>=2.9.1; extra == 'fastapi'
9
10
  Provides-Extra: sqlalchemy
10
11
  Requires-Dist: sqlalchemy>=2.0.40; extra == 'sqlalchemy'
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "u-toolkit"
3
- version = "0.1.1"
3
+ version = "0.1.2"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -16,6 +16,7 @@ sqlalchemy = [
16
16
  "sqlalchemy>=2.0.40",
17
17
  ]
18
18
  fastapi = [
19
+ "fastapi>=0.115.12",
19
20
  "pydantic-settings>=2.9.1",
20
21
  ]
21
22
 
@@ -26,13 +27,12 @@ build-backend = "hatchling.build"
26
27
  [dependency-groups]
27
28
  dev = [
28
29
  "devtools>=0.12.2",
30
+ "httpx>=0.28.1",
29
31
  "pytest>=8.3.5",
32
+ "python-multipart>=0.0.20",
30
33
  "ruff>=0.11.6",
31
34
  "uvicorn>=0.34.2",
32
35
  ]
33
- fastapi = [
34
- "fastapi>=0.115.12",
35
- ]
36
36
 
37
37
 
38
38
 
@@ -27,7 +27,8 @@ class DefineMethodDecorator(Generic[_T, _FnT]):
27
27
  self.register_method(DefineMethodParams(owner_class, name, self.fn))
28
28
 
29
29
  def __get__(self, instance: _T, owner_class: type[_T]):
30
- update_parameters(self.fn, *list_parameters(self.fn)[1:])
30
+ parameters = list_parameters(self.fn)[1:]
31
+ update_parameters(self.fn, *parameters)
31
32
 
32
33
  @wraps(self.fn)
33
34
  def wrapper(*args, **kwargs):
@@ -296,26 +296,31 @@ class CBV:
296
296
  )
297
297
  for name, dep in iter_dependencies(cls)
298
298
  ]
299
+
299
300
  update_parameters(collect_cls_dependencies, *parameters)
300
301
 
301
302
  def decorator(method: Callable):
302
- sign_fn = partial(method)
303
- update_wrapper(sign_fn, method)
303
+ method_name = method.__name__
304
+
305
+ cls_fn = getattr(cls, method_name)
306
+ sign_cls_fn = partial(cls_fn)
307
+ update_wrapper(sign_cls_fn, cls_fn)
304
308
 
305
309
  parameters, *_ = with_parameter(
306
- method,
310
+ sign_cls_fn,
307
311
  name=collect_cls_dependencies.__name__,
308
312
  default=Depends(collect_cls_dependencies),
309
313
  )
310
- update_parameters(sign_fn, *parameters)
311
314
 
312
- @wraps(sign_fn)
315
+ update_parameters(sign_cls_fn, *(parameters[1:]))
316
+
317
+ @wraps(sign_cls_fn)
313
318
  def wrapper(*args, **kwargs):
314
319
  instance = self._build_cls(cls)
315
320
  dependencies = kwargs.pop(collect_cls_dependencies.__name__)
316
321
  for dep_name, dep_value in dependencies.items():
317
322
  setattr(instance, dep_name, dep_value)
318
- fn = getattr(instance, method.__name__)
323
+ fn = getattr(instance, method_name)
319
324
  return fn(*args, **kwargs)
320
325
 
321
326
  return wrapper
@@ -57,13 +57,12 @@ def update_signature(
57
57
  return_annotation: type | None = None,
58
58
  ):
59
59
  signature = inspect.signature(fn)
60
- kwargs = {}
61
60
  if parameters:
62
- kwargs["parameters"] = parameters
61
+ signature = signature.replace(parameters=parameters)
63
62
  if return_annotation:
64
- kwargs["return_annotation"] = return_annotation
63
+ signature = signature.replace(return_annotation=return_annotation)
65
64
 
66
- fn.__signature__ = signature.replace(**kwargs) # type: ignore
65
+ setattr(fn, "__signature__", signature)
67
66
 
68
67
 
69
68
  def update_parameters(fn: Callable, *parameters: inspect.Parameter):
File without changes
File without changes
@@ -0,0 +1,94 @@
1
+ from typing import Annotated, cast
2
+
3
+ import sqlalchemy as sa
4
+ from fastapi import Depends, FastAPI, status
5
+ from fastapi.security import OAuth2PasswordRequestForm
6
+ from fastapi.testclient import TestClient
7
+
8
+ from u_toolkit.fastapi.cbv import CBV
9
+
10
+
11
+ cbv = CBV()
12
+
13
+
14
+ def gen_value():
15
+ return id(object())
16
+
17
+
18
+ dep1_value = gen_value()
19
+
20
+
21
+ def dep1():
22
+ return dep1_value
23
+
24
+
25
+ dep2_value = gen_value()
26
+
27
+
28
+ def dep2():
29
+ return dep2_value
30
+
31
+
32
+ dep3_value = gen_value()
33
+
34
+
35
+ def dep3():
36
+ yield dep3_value
37
+
38
+
39
+ def db():
40
+ with sa.create_engine(
41
+ "sqlite+pysqlite:///:memory:", echo=True, future=True
42
+ ).connect() as conn:
43
+ yield conn
44
+
45
+
46
+ RESULT = "hello world"
47
+
48
+
49
+ @cbv
50
+ class R:
51
+ value = Depends(dep1)
52
+ value2: Annotated[int, Depends(dep2)]
53
+ value3 = Depends(dep3)
54
+ db_dep = cast(sa.Connection, Depends(db))
55
+
56
+ def get(self):
57
+ return self.value, self.value2, self.value3
58
+
59
+ @cbv.info(status=status.HTTP_201_CREATED)
60
+ def post(self):
61
+ record = self.db_dep.execute(sa.text(f"select '{RESULT}'")).one()
62
+ return record[0]
63
+
64
+
65
+ @cbv
66
+ class _NoPath:
67
+ value = Depends(dep1)
68
+
69
+ def post(self, data: Annotated[OAuth2PasswordRequestForm, Depends()]):
70
+ return data.username
71
+
72
+
73
+ app = FastAPI()
74
+
75
+
76
+ app.include_router(cbv.router)
77
+ client = TestClient(app)
78
+
79
+
80
+ def test_cbv():
81
+ assert client.get("/r").json() == [dep1_value, dep2_value, dep3_value]
82
+
83
+ post_resp = client.post("/r")
84
+ assert post_resp.status_code == status.HTTP_201_CREATED
85
+ assert post_resp.json() == RESULT
86
+
87
+ value = "example"
88
+ assert (
89
+ client.post(
90
+ "/",
91
+ data={"username": value, "password": value},
92
+ ).json()
93
+ == value
94
+ )
File without changes
File without changes
File without changes