arpakitlib 1.8.258__py3-none-any.whl → 1.8.260__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.
@@ -1,4 +1,3 @@
1
- from __future__ import annotations
2
1
 
3
2
  import datetime as dt
4
3
  from typing import TYPE_CHECKING, Any
@@ -92,14 +91,14 @@ class UserDBM(SimpleDBM):
92
91
  )
93
92
 
94
93
  # many to one
95
- user_tokens: Mapped[list[UserTokenDBM]] = relationship(
94
+ user_tokens: Mapped[list["UserTokenDBM"]] = relationship(
96
95
  "UserTokenDBM",
97
96
  uselist=True,
98
97
  back_populates="user",
99
98
  foreign_keys="UserTokenDBM.user_id",
100
99
  cascade="all, delete-orphan"
101
100
  )
102
- verification_codes: Mapped[list[VerificationCodeDBM]] = relationship(
101
+ verification_codes: Mapped[list["VerificationCodeDBM"]] = relationship(
103
102
  "VerificationCodeDBM",
104
103
  uselist=True,
105
104
  back_populates="user",
@@ -1,47 +1,30 @@
1
1
  # arpakit
2
- import datetime as dt
3
- from typing import Any, Optional, get_type_hints, get_origin, Union, Annotated, get_args
2
+ from typing import Any, get_type_hints, get_origin, Union, Annotated, get_args
4
3
 
5
- from pydantic import BaseModel, Field, ConfigDict
4
+ from pydantic import BaseModel, ConfigDict
6
5
  from sqlalchemy import inspect
7
- from sqlalchemy.orm import ColumnProperty
8
- from sqlalchemy.sql.sqltypes import (
9
- Boolean, Integer, BigInteger, SmallInteger,
10
- String, Text, Unicode, UnicodeText,
11
- DateTime, Date, Time,
12
- Float, Numeric, DECIMAL, LargeBinary, JSON
13
- )
6
+ from sqlalchemy.orm import ColumnProperty, Mapped
7
+
8
+ from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
14
9
 
15
10
  _ARPAKIT_LIB_MODULE_VERSION = "3.0"
16
11
 
17
- _SQLA_TYPE_MAP = {
18
- Boolean: bool,
19
- Integer: int,
20
- BigInteger: int,
21
- SmallInteger: int,
22
- Float: float,
23
- Numeric: float,
24
- DECIMAL: float,
25
- String: str,
26
- Unicode: str,
27
- Text: str,
28
- UnicodeText: str,
29
- LargeBinary: bytes,
30
- JSON: dict | list,
31
- DateTime: dt.datetime,
32
- Date: dt.date,
33
- Time: dt.time,
34
- }
35
-
36
-
37
- def _python_type_from_col(col) -> type | str:
38
- try:
39
- return col.type.python_type
40
- except Exception:
41
- for sa_t, py_t in _SQLA_TYPE_MAP.items():
42
- if isinstance(col.type, sa_t):
43
- return py_t
44
- return Any
12
+
13
+ def _define_sqlalchemy_column_mapped_type(type_: Any) -> Any:
14
+ """
15
+ Возвращает тип колонки ИСКЛЮЧИТЕЛЬНО из аннотации поля.
16
+ Разворачивает оболочки:
17
+ - Annotated[T, ...] -> T
18
+ - Mapped[T] -> T
19
+ Если аннотации нет — возвращает Any.
20
+ """
21
+
22
+ origin = get_origin(type_)
23
+
24
+ if origin is Mapped:
25
+ return get_args(type_)[0] if get_args(type_) else Any
26
+
27
+ return type_
45
28
 
46
29
 
47
30
  def _get_property_name_to_type_from_model_class(
@@ -64,8 +47,9 @@ def _get_property_name_to_type_from_model_class(
64
47
  ret_type = hints.get("return", Any)
65
48
  except Exception:
66
49
  if skip_property_if_cannot_define_type:
67
- continue
68
- ret_type = Any
50
+ ret_type = Any
51
+ else:
52
+ raise
69
53
  if exclude_property_names:
70
54
  if property_name in exclude_property_names:
71
55
  continue
@@ -108,12 +92,22 @@ def _type_matches(*, type_: Any, allowed_types: list[type]) -> bool:
108
92
  return False
109
93
 
110
94
 
95
+ def _get_sqlalchemy_mapped_types(sqlalchemy_model):
96
+ result = {}
97
+ for cls in reversed(sqlalchemy_model.__mro__):
98
+ annotations = getattr(cls, "__annotations__", {})
99
+ for field, annotation in annotations.items():
100
+ if get_origin(annotation) is Mapped:
101
+ result[field] = annotation
102
+ return result
103
+
104
+
111
105
  def pydantic_schema_from_sqlalchemy_model(
112
106
  sqlalchemy_model: type,
113
107
  *,
114
108
  model_name: str | None = None,
115
109
  base_model: type[BaseModel] = BaseModel,
116
- include_column_defaults: bool = False,
110
+ include_columns: bool = True,
117
111
  exclude_column_names: list[str] | None = None,
118
112
  include_properties: bool = False,
119
113
  include_property_names: list[str] | None = None,
@@ -148,31 +142,15 @@ def pydantic_schema_from_sqlalchemy_model(
148
142
  remove_property_prefixes = set(remove_property_prefixes or [])
149
143
 
150
144
  # 1) Колонки
151
- for prop in mapper.attrs:
152
- if not isinstance(prop, ColumnProperty):
153
- continue
154
- if prop.key in exclude_column_names:
155
- continue
156
-
157
- column = prop.columns[0]
158
- column_type = _python_type_from_col(column)
159
-
160
- # Аннотация типа
161
- if column.nullable:
162
- annotations[prop.key] = Optional[column_type] # type: ignore[name-defined]
163
- else:
164
- annotations[prop.key] = column_type
165
-
166
- # Дефолты, если нужно
167
- if include_column_defaults:
168
- default_value = None
169
- if column.default is not None and getattr(column.default, "is_scalar", False):
170
- default_value = column.default.arg
171
- elif column.server_default is not None and getattr(column.server_default.arg, "text", None):
172
- default_value = column.server_default.arg.text
145
+ if include_columns:
173
146
 
174
- if default_value is not None:
175
- attrs[prop.key] = Field(default=default_value)
147
+ for column_attr in mapper.column_attrs:
148
+ if not isinstance(column_attr, ColumnProperty):
149
+ continue
150
+ if column_attr.key in exclude_column_names:
151
+ continue
152
+ mapped_type = _get_sqlalchemy_mapped_types(sqlalchemy_model=sqlalchemy_model)[column_attr.key]
153
+ annotations[column_attr.key] = _define_sqlalchemy_column_mapped_type(type_=mapped_type)
176
154
 
177
155
  # 2) Свойства (@property)
178
156
  if include_properties:
@@ -260,3 +238,6 @@ def pydantic_schema_from_sqlalchemy_model(
260
238
  attrs["model_config"] = ConfigDict(extra="ignore", arbitrary_types_allowed=True, from_attributes=True)
261
239
 
262
240
  return type(model_name, (base_model,), attrs)
241
+
242
+
243
+ print(pydantic_schema_from_sqlalchemy_model(sqlalchemy_model=UserDBM))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: arpakitlib
3
- Version: 1.8.258
3
+ Version: 1.8.260
4
4
  Summary: arpakitlib
5
5
  License: Apache-2.0
6
6
  Keywords: arpakitlib,arpakit,arpakit-company,arpakitcompany,arpakit_company
@@ -275,7 +275,7 @@ arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model
275
275
  arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/common.py,sha256=NsZbGGJvsn1IgfUQ9J4_6qBQyNBADDmt2Q8waRePD0c,5851
276
276
  arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/operation.py,sha256=6mDtD20H0bQEoSwReLPzaqNp6vLHgOQ7D1DD7KC7zIU,7111
277
277
  arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/story_log.py,sha256=Xou8XvEGQfLuEHH5RKdq6pXCZWMhmtyshQvvohT-Go8,2507
278
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/user.py,sha256=22TE8y2Vm9byLLkIE6RVAMa29d9ILCVg1e1QYi4ONIQ,7898
278
+ arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/user.py,sha256=rLR4uWTS-tCX2SMVy8YrkMI-01b5kXEhaBHDbrLmmFI,7867
279
279
  arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/user_token.py,sha256=Yy4XZLHJqNEt_SlQOcVCDwHqxpuBwtOmyOfGKbYqKeo,1988
280
280
  arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/verification_code.py,sha256=xM5e9C8ELI1RJBAaSXZPn9Qpwl4DT2TgRvbYOxsKZsM,3137
281
281
  arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -414,7 +414,7 @@ arpakitlib/ar_need_type_util.py,sha256=XmY1kswz8j9oo5f9CxRu0_zgfvxWrXPYKOj6MM04s
414
414
  arpakitlib/ar_openai_api_client_util.py,sha256=dWgsSPXtxNBxS5VRi_NharGQrUXF_YjIfhU3Bj5cW9M,5651
415
415
  arpakitlib/ar_parse_command.py,sha256=1WTdQoWVshoDZ1jDaKeTzajfqaYHP3FNO0-REyo1aMY,3003
416
416
  arpakitlib/ar_postgresql_util.py,sha256=1AuLjEaa1Lg4pzn-ukCVnDi35Eg1k91APRTqZhIJAdo,945
417
- arpakitlib/ar_pydantic_schema_from_sqlalchemy_model.py,sha256=yOIgvrFeRm_MUwK7dc1sA_DD5yTKKAFb2HxF4zyGof8,10801
417
+ arpakitlib/ar_pydantic_schema_from_sqlalchemy_model.py,sha256=0OXyyN0f08N5k5SwZOhqZnRnkU38ReCBDIDd3qDarlY,10403
418
418
  arpakitlib/ar_raise_own_exception_if_exception.py,sha256=A6TuNSBk1pHaQ_qxnUmE2LgsNGA1IGqX26b1_HEA4Nc,5978
419
419
  arpakitlib/ar_rat_func_util.py,sha256=Ca10o3RJwyx_DJLxjTxgHDO6NU3M6CWgUR4bif67OE4,2006
420
420
  arpakitlib/ar_really_validate_email.py,sha256=HBfhyiDB3INI6Iq6hR2WOMKA5wVWWRl0Qun-x__OZ9o,1201
@@ -430,8 +430,8 @@ arpakitlib/ar_sqlalchemy_util.py,sha256=FDva9onjtCPrYZYIHHb93NMwD1WlmaORjiWgPRJQ
430
430
  arpakitlib/ar_str_util.py,sha256=2lGpnXDf2h1cBZpVf5i1tX_HCv5iBd6IGnrCw4QWWlY,4350
431
431
  arpakitlib/ar_type_util.py,sha256=Cs_tef-Fc5xeyAF54KgISCsP11NHyzIsglm4S3Xx7iM,4049
432
432
  arpakitlib/ar_yookassa_api_client_util.py,sha256=VozuZeCJjmLd1zj2BdC9WfiAQ3XYOrIMsdpNK-AUlm0,5347
433
- arpakitlib-1.8.258.dist-info/LICENSE,sha256=GPEDQMam2r7FSTYqM1mm7aKnxLaWcBotH7UvQtea-ec,11355
434
- arpakitlib-1.8.258.dist-info/METADATA,sha256=TmbE6VOMn-T80eJUfQdCRpVbrurBEodE_x1U_o_UDWU,3919
435
- arpakitlib-1.8.258.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
436
- arpakitlib-1.8.258.dist-info/entry_points.txt,sha256=36xqR3PJFT2kuwjkM_EqoIy0qFUDPKSm_mJaI7emewE,87
437
- arpakitlib-1.8.258.dist-info/RECORD,,
433
+ arpakitlib-1.8.260.dist-info/LICENSE,sha256=GPEDQMam2r7FSTYqM1mm7aKnxLaWcBotH7UvQtea-ec,11355
434
+ arpakitlib-1.8.260.dist-info/METADATA,sha256=8iFJMcmzoK7qqnHIMzadbyvpFOZIWiF6xdYSj9wuA2o,3919
435
+ arpakitlib-1.8.260.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
436
+ arpakitlib-1.8.260.dist-info/entry_points.txt,sha256=36xqR3PJFT2kuwjkM_EqoIy0qFUDPKSm_mJaI7emewE,87
437
+ arpakitlib-1.8.260.dist-info/RECORD,,