sqlmodel-mypy-plugin 0.16.0__tar.gz → 0.19.0__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 (110) hide show
  1. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/.gitignore +2 -0
  2. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/AGENTS.md +5 -2
  3. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/LICENSE +1 -1
  4. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/PKG-INFO +29 -6
  5. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/README.md +25 -2
  6. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/pyproject.toml +3 -3
  7. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/src/sqlmodel_mypy/__init__.py +1 -1
  8. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/src/sqlmodel_mypy/plugin.py +654 -60
  9. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/src/sqlmodel_mypy/transform.py +72 -0
  10. sqlmodel_mypy_plugin-0.19.0/tests/mypy/configs/mypy-plugin-strict-typed-execute.ini +20 -0
  11. sqlmodel_mypy_plugin-0.19.0/tests/mypy/modules/aliased_joins.py +32 -0
  12. sqlmodel_mypy_plugin-0.19.0/tests/mypy/modules/field_validation_alias.py +10 -0
  13. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/getattr_column_property.py +16 -0
  14. sqlmodel_mypy_plugin-0.19.0/tests/mypy/modules/select_varargs.py +36 -0
  15. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/session_exec.py +9 -2
  16. sqlmodel_mypy_plugin-0.19.0/tests/mypy/modules/session_execute_typed.py +32 -0
  17. sqlmodel_mypy_plugin-0.19.0/tests/mypy/modules/table_dunders.py +24 -0
  18. sqlmodel_mypy_plugin-0.19.0/tests/mypy/modules/table_via_model_config.py +35 -0
  19. sqlmodel_mypy_plugin-0.19.0/tests/mypy/modules/tuple_expression.py +14 -0
  20. sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin-strict-typed-execute_ini/session_execute_typed.py +35 -0
  21. sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin-strict_ini/aliased_joins.py +32 -0
  22. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/docs_parity_read_connected_data.py +1 -1
  23. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/execution_options.py +0 -1
  24. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/joins.py +0 -3
  25. sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin-strict_ini/select_varargs.py +37 -0
  26. sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin-strict_ini/table_via_model_config.py +37 -0
  27. sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin-strict_ini/tuple_expression.py +14 -0
  28. sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin_ini/aliased_joins.py +32 -0
  29. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/execution_options.py +0 -1
  30. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/joins.py +0 -3
  31. sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin_ini/select_varargs.py +37 -0
  32. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/test_mypy.py +7 -0
  33. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/test_plugin_unit.py +940 -0
  34. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/test_transform_unit.py +42 -0
  35. sqlmodel_mypy_plugin-0.16.0/ROADMAP.md +0 -311
  36. sqlmodel_mypy_plugin-0.16.0/tests/mypy/modules/select_varargs.py +0 -18
  37. sqlmodel_mypy_plugin-0.16.0/tests/mypy/modules/table_dunders.py +0 -12
  38. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/.pre-commit-config.yaml +0 -0
  39. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/src/sqlmodel_mypy/persisted.py +0 -0
  40. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/src/sqlmodel_mypy/py.typed +0 -0
  41. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/conftest.py +0 -0
  42. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/configs/mypy-plugin-no-untyped.ini +0 -0
  43. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/configs/mypy-plugin-pydantic-first.ini +0 -0
  44. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/configs/mypy-plugin-pydantic-last.ini +0 -0
  45. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/configs/mypy-plugin-strict-no-explicit-any.ini +0 -0
  46. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/configs/mypy-plugin-strict.ini +0 -0
  47. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/configs/mypy-plugin.ini +0 -0
  48. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/configs/pyproject-plugin.toml +0 -0
  49. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/aliases.py +0 -0
  50. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/annotated_field_metadata.py +0 -0
  51. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/constructor.py +0 -0
  52. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/docs_parity_many_to_many_create_data.py +0 -0
  53. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/docs_parity_read_connected_data.py +0 -0
  54. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/docs_parity_relationship_attributes.py +0 -0
  55. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/docs_parity_update_patterns.py +0 -0
  56. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/execution_options.py +0 -0
  57. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/expression_typing_strict.py +0 -0
  58. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/expressions.py +0 -0
  59. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/field_sa_type.py +0 -0
  60. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/generics.py +0 -0
  61. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/inheritance.py +0 -0
  62. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/joins.py +0 -0
  63. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/label.py +0 -0
  64. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/many_to_many_relationships.py +0 -0
  65. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/mapped_fields.py +0 -0
  66. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/model_config.py +0 -0
  67. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/model_construct.py +0 -0
  68. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/nullable_defaults.py +0 -0
  69. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/persisted_helpers.py +0 -0
  70. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/relationship_comparators.py +0 -0
  71. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/relationship_expressions.py +0 -0
  72. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/relationships.py +0 -0
  73. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/selectinload.py +0 -0
  74. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/session_get.py +0 -0
  75. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/table_model_awareness.py +0 -0
  76. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/untyped_fields.py +0 -0
  77. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-pydantic-first_ini/relationships.py +0 -0
  78. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-pydantic-last_ini/relationships.py +0 -0
  79. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/aliases.py +0 -0
  80. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/annotated_field_metadata.py +0 -0
  81. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/constructor.py +0 -0
  82. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/docs_parity_many_to_many_create_data.py +0 -0
  83. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/docs_parity_relationship_attributes.py +0 -0
  84. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/docs_parity_update_patterns.py +0 -0
  85. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/generics.py +0 -0
  86. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/label.py +0 -0
  87. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/many_to_many_relationships.py +0 -0
  88. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/mapped_fields.py +0 -0
  89. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/model_construct.py +0 -0
  90. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/nullable_defaults.py +0 -0
  91. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/persisted_helpers.py +0 -0
  92. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/relationship_comparators.py +0 -0
  93. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/relationships.py +0 -0
  94. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/table_model_awareness.py +0 -0
  95. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/aliases.py +0 -0
  96. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/constructor.py +0 -0
  97. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/expressions.py +0 -0
  98. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/inheritance.py +0 -0
  99. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/label.py +0 -0
  100. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/many_to_many_relationships.py +0 -0
  101. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/model_construct.py +0 -0
  102. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/nullable_defaults.py +0 -0
  103. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/relationship_comparators.py +0 -0
  104. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/relationship_expressions.py +0 -0
  105. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/relationships.py +0 -0
  106. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/table_model_awareness.py +0 -0
  107. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/untyped_fields.py +0 -0
  108. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/pyproject-plugin_toml/constructor.py +0 -0
  109. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/smoke_test.py +0 -0
  110. {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/test_persisted_helpers.py +0 -0
@@ -21,3 +21,5 @@ ENV/
21
21
 
22
22
  dist/
23
23
  build/
24
+
25
+ excl-*
@@ -47,8 +47,11 @@ make update-mypy
47
47
  ## What the plugin does (scope)
48
48
 
49
49
  - Collects SQLModel fields and determines required/optional kwargs based on `sqlmodel.Field(...)`.
50
- - **Ignores** `sqlmodel.Relationship(...)` when generating constructor kwargs.
50
+ - For `table=True` models, includes `sqlmodel.Relationship(...)` members as **optional** constructor kwargs.
51
51
  - Synthesizes `__init__` and `model_construct` signatures for SQLModel subclasses during mypy semantic analysis.
52
+ - Adjusts `table=True` SQLModel class attribute types to behave like SQLAlchemy expressions (e.g. `User.id` and relationship expression/comparator patterns).
53
+ - Exposes SQLAlchemy table metadata on `table=True` models (e.g. `Model.__table__`).
54
+ - Adds targeted typing fixes for common query patterns/helpers (`select(...)` 5+ entities fallback, `sqlmodel.tuple_(...)` as an expression, outer-join `None` propagation, broadened `Session.exec(...)`, optional typed `Session.execute(...)`, and best-effort `column_property(...)` return typing).
52
55
 
53
56
  ## Repo layout (where things live)
54
57
 
@@ -83,4 +86,4 @@ make update-mypy
83
86
  - To release:
84
87
  - Bump the version.
85
88
  - Tag `vX.Y.Z` and push the tag.
86
- - GitHub Actions publishes to PyPI via **trusted publishing** (requires configuring the PyPI project + GitHub environment `pypi`).
89
+ - GitHub Actions publishes to PyPI via **trusted publishing**
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Artem
3
+ Copyright (c) 2026 sertione
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlmodel-mypy-plugin
3
- Version: 0.16.0
3
+ Version: 0.19.0
4
4
  Summary: Mypy plugin for SQLModel (better constructor/Field typing).
5
5
  Project-URL: Homepage, https://github.com/sertione/sqlmodel-mypy-plugin
6
6
  Project-URL: Repository, https://github.com/sertione/sqlmodel-mypy-plugin
7
7
  Project-URL: Issues, https://github.com/sertione/sqlmodel-mypy-plugin/issues
8
8
  Project-URL: Changelog, https://github.com/sertione/sqlmodel-mypy-plugin/releases
9
- Author: Artem
9
+ Author: sertione
10
10
  License: MIT License
11
11
 
12
- Copyright (c) 2026 Artem
12
+ Copyright (c) 2026 sertione
13
13
 
14
14
  Permission is hereby granted, free of charge, to any person obtaining a copy
15
15
  of this software and associated documentation files (the "Software"), to deal
@@ -30,7 +30,7 @@ License: MIT License
30
30
  SOFTWARE.
31
31
  License-File: LICENSE
32
32
  Keywords: mypy,plugin,pydantic,sqlalchemy,sqlmodel,typing
33
- Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Development Status :: 4 - Beta
34
34
  Classifier: Intended Audience :: Developers
35
35
  Classifier: License :: OSI Approved :: MIT License
36
36
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -68,11 +68,18 @@ Planned work is tracked in [`ROADMAP.md`](ROADMAP.md).
68
68
  - Outer-join `None` propagation in `select(A, B).join(B, isouter=True)` result tuples.
69
69
  - Broaden `Session.exec()` / `AsyncSession.exec()` typing to accept SQLAlchemy `Executable` statements (e.g.
70
70
  `text(...)`), not just `select(...)`.
71
+ - Optional typed `Session.execute()` / `AsyncSession.execute()` for `select(...)` (opt-in; see `typed_execute`).
71
72
  - Accept SQLAlchemy TypeEngine instances in `Field(sa_type=...)` (e.g. `DateTime(timezone=True)`, `String(50)`)
72
73
  without `# type: ignore` in strict mode.
73
74
  - Accept `model_config = ConfigDict(...)` overrides on SQLModel subclasses in strict mode.
74
75
  - Compatible with `pydantic.mypy` **when `sqlmodel_mypy.plugin` is listed first**.
75
76
 
77
+ ## Install
78
+
79
+ ```bash
80
+ uv add sqlmodel-mypy-plugin
81
+ ```
82
+
76
83
  ## Install (dev)
77
84
 
78
85
  Prereqs: **Python 3.10+** and [`uv`](https://docs.astral.sh/uv/).
@@ -119,6 +126,9 @@ Supported options:
119
126
  `__init__` / `model_construct` / constructor signatures, so mypy reports unexpected keyword arguments.
120
127
  - **`warn_untyped_fields`** (default: `true`): When `true`, emit error code `sqlmodel-field` for untyped
121
128
  declarations like `x = Field(...)` / `x = Relationship(...)` (use `x: T = ...` instead).
129
+ - **`typed_execute`** (default: `false`): When `true`, type SQLModel’s deprecated `Session.execute()` /
130
+ `AsyncSession.execute()` for common `select(...)` statements by recovering `Result[...]` generics. This is meant to
131
+ reduce “type clutter” during migrations; prefer `Session.exec()` for new code.
122
132
  - **`debug_dataclass_transform`** (default: `false`): **Advanced/debug-only**. When `true`, keep SQLModel’s
123
133
  `__dataclass_transform__` handling enabled in mypy (useful for debugging plugin interactions; not recommended
124
134
  for normal use).
@@ -130,6 +140,7 @@ Supported options:
130
140
  init_typed = false
131
141
  init_forbid_extra = false
132
142
  warn_untyped_fields = true
143
+ typed_execute = false
133
144
  debug_dataclass_transform = false
134
145
  ```
135
146
 
@@ -140,12 +151,15 @@ debug_dataclass_transform = false
140
151
  init_typed = false
141
152
  init_forbid_extra = false
142
153
  warn_untyped_fields = true
154
+ typed_execute = false
143
155
  debug_dataclass_transform = false
144
156
  ```
145
157
 
146
158
  ## Error codes
147
159
 
148
160
  - `sqlmodel-field`: field-related plugin errors (e.g. untyped `x = Field(...)`).
161
+ - `sqlmodel-plugin-order`: plugin ordering errors (e.g. `pydantic.mypy` enabled before `sqlmodel_mypy.plugin`).
162
+ Fix: list `sqlmodel_mypy.plugin` first (see [Enable in mypy](#enable-in-mypy)).
149
163
 
150
164
  ## SQL expression typing
151
165
 
@@ -170,13 +184,22 @@ tbl = User.__table__
170
184
 
171
185
  ### `getattr(Model, "field")` support
172
186
 
173
- If you build query filters dynamically, `getattr(Model, "field")` with a **string literal** is typed the same as
174
- direct `Model.field` access on `table=True` models:
187
+ If you build query filters dynamically, `getattr(Model, "field")` with a **string literal** (or a `Final` / string
188
+ `Literal[...]` name) is typed the same as direct `Model.field` access on `table=True` models:
175
189
 
176
190
  ```py
177
191
  stmt = select(User).where(getattr(User, "name").like("%x%"))
178
192
  ```
179
193
 
194
+ Example using a `Final` constant:
195
+
196
+ ```py
197
+ from typing import Final
198
+
199
+ FIELD: Final = "name"
200
+ stmt = select(User).where(getattr(User, FIELD).like("%x%"))
201
+ ```
202
+
180
203
  Non-literal names (runtime strings) are intentionally left as `Any` (you’ll still need a cast or a different
181
204
  pattern).
182
205
 
@@ -22,11 +22,18 @@ Planned work is tracked in [`ROADMAP.md`](ROADMAP.md).
22
22
  - Outer-join `None` propagation in `select(A, B).join(B, isouter=True)` result tuples.
23
23
  - Broaden `Session.exec()` / `AsyncSession.exec()` typing to accept SQLAlchemy `Executable` statements (e.g.
24
24
  `text(...)`), not just `select(...)`.
25
+ - Optional typed `Session.execute()` / `AsyncSession.execute()` for `select(...)` (opt-in; see `typed_execute`).
25
26
  - Accept SQLAlchemy TypeEngine instances in `Field(sa_type=...)` (e.g. `DateTime(timezone=True)`, `String(50)`)
26
27
  without `# type: ignore` in strict mode.
27
28
  - Accept `model_config = ConfigDict(...)` overrides on SQLModel subclasses in strict mode.
28
29
  - Compatible with `pydantic.mypy` **when `sqlmodel_mypy.plugin` is listed first**.
29
30
 
31
+ ## Install
32
+
33
+ ```bash
34
+ uv add sqlmodel-mypy-plugin
35
+ ```
36
+
30
37
  ## Install (dev)
31
38
 
32
39
  Prereqs: **Python 3.10+** and [`uv`](https://docs.astral.sh/uv/).
@@ -73,6 +80,9 @@ Supported options:
73
80
  `__init__` / `model_construct` / constructor signatures, so mypy reports unexpected keyword arguments.
74
81
  - **`warn_untyped_fields`** (default: `true`): When `true`, emit error code `sqlmodel-field` for untyped
75
82
  declarations like `x = Field(...)` / `x = Relationship(...)` (use `x: T = ...` instead).
83
+ - **`typed_execute`** (default: `false`): When `true`, type SQLModel’s deprecated `Session.execute()` /
84
+ `AsyncSession.execute()` for common `select(...)` statements by recovering `Result[...]` generics. This is meant to
85
+ reduce “type clutter” during migrations; prefer `Session.exec()` for new code.
76
86
  - **`debug_dataclass_transform`** (default: `false`): **Advanced/debug-only**. When `true`, keep SQLModel’s
77
87
  `__dataclass_transform__` handling enabled in mypy (useful for debugging plugin interactions; not recommended
78
88
  for normal use).
@@ -84,6 +94,7 @@ Supported options:
84
94
  init_typed = false
85
95
  init_forbid_extra = false
86
96
  warn_untyped_fields = true
97
+ typed_execute = false
87
98
  debug_dataclass_transform = false
88
99
  ```
89
100
 
@@ -94,12 +105,15 @@ debug_dataclass_transform = false
94
105
  init_typed = false
95
106
  init_forbid_extra = false
96
107
  warn_untyped_fields = true
108
+ typed_execute = false
97
109
  debug_dataclass_transform = false
98
110
  ```
99
111
 
100
112
  ## Error codes
101
113
 
102
114
  - `sqlmodel-field`: field-related plugin errors (e.g. untyped `x = Field(...)`).
115
+ - `sqlmodel-plugin-order`: plugin ordering errors (e.g. `pydantic.mypy` enabled before `sqlmodel_mypy.plugin`).
116
+ Fix: list `sqlmodel_mypy.plugin` first (see [Enable in mypy](#enable-in-mypy)).
103
117
 
104
118
  ## SQL expression typing
105
119
 
@@ -124,13 +138,22 @@ tbl = User.__table__
124
138
 
125
139
  ### `getattr(Model, "field")` support
126
140
 
127
- If you build query filters dynamically, `getattr(Model, "field")` with a **string literal** is typed the same as
128
- direct `Model.field` access on `table=True` models:
141
+ If you build query filters dynamically, `getattr(Model, "field")` with a **string literal** (or a `Final` / string
142
+ `Literal[...]` name) is typed the same as direct `Model.field` access on `table=True` models:
129
143
 
130
144
  ```py
131
145
  stmt = select(User).where(getattr(User, "name").like("%x%"))
132
146
  ```
133
147
 
148
+ Example using a `Final` constant:
149
+
150
+ ```py
151
+ from typing import Final
152
+
153
+ FIELD: Final = "name"
154
+ stmt = select(User).where(getattr(User, FIELD).like("%x%"))
155
+ ```
156
+
134
157
  Non-literal names (runtime strings) are intentionally left as `Any` (you’ll still need a cast or a different
135
158
  pattern).
136
159
 
@@ -4,15 +4,15 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sqlmodel-mypy-plugin"
7
- version = "0.16.0"
7
+ version = "0.19.0"
8
8
  description = "Mypy plugin for SQLModel (better constructor/Field typing)."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  license = { file = "LICENSE" }
12
- authors = [{ name = "Artem" }]
12
+ authors = [{ name = "sertione" }]
13
13
  keywords = ["mypy", "plugin", "sqlmodel", "pydantic", "sqlalchemy", "typing"]
14
14
  classifiers = [
15
- "Development Status :: 3 - Alpha",
15
+ "Development Status :: 4 - Beta",
16
16
  "Intended Audience :: Developers",
17
17
  "License :: OSI Approved :: MIT License",
18
18
  "Programming Language :: Python :: 3 :: Only",
@@ -1,4 +1,4 @@
1
- __version__ = "0.16.0"
1
+ __version__ = "0.19.0"
2
2
 
3
3
  from .persisted import HasId, HasOptionalId, has_id, require_id
4
4