sqlmodel-mypy-plugin 0.19.0__tar.gz → 0.19.1__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.
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/PKG-INFO +1 -3
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/README.md +0 -2
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/pyproject.toml +1 -1
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/src/sqlmodel_mypy/__init__.py +1 -1
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/src/sqlmodel_mypy/plugin.py +1 -29
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/src/sqlmodel_mypy/transform.py +22 -4
- {sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin_ini → sqlmodel_mypy_plugin-0.19.1/tests/mypy/outputs/mypy-plugin-strict_ini}/aliased_joins.py +1 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/docs_parity_read_connected_data.py +1 -1
- {sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin_ini → sqlmodel_mypy_plugin-0.19.1/tests/mypy/outputs/mypy-plugin-strict_ini}/execution_options.py +1 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/joins.py +3 -0
- sqlmodel_mypy_plugin-0.19.1/tests/mypy/outputs/mypy-plugin-strict_ini/session_exec.py +43 -0
- {sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin-strict_ini → sqlmodel_mypy_plugin-0.19.1/tests/mypy/outputs/mypy-plugin_ini}/aliased_joins.py +1 -0
- {sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin-strict_ini → sqlmodel_mypy_plugin-0.19.1/tests/mypy/outputs/mypy-plugin_ini}/execution_options.py +1 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin_ini/joins.py +3 -0
- sqlmodel_mypy_plugin-0.19.1/tests/mypy/outputs/mypy-plugin_ini/session_exec.py +43 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/test_transform_unit.py +52 -1
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/.gitignore +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/.pre-commit-config.yaml +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/AGENTS.md +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/LICENSE +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/src/sqlmodel_mypy/persisted.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/src/sqlmodel_mypy/py.typed +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/conftest.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/configs/mypy-plugin-no-untyped.ini +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/configs/mypy-plugin-pydantic-first.ini +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/configs/mypy-plugin-pydantic-last.ini +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/configs/mypy-plugin-strict-no-explicit-any.ini +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/configs/mypy-plugin-strict-typed-execute.ini +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/configs/mypy-plugin-strict.ini +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/configs/mypy-plugin.ini +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/configs/pyproject-plugin.toml +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/aliased_joins.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/aliases.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/annotated_field_metadata.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/constructor.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/docs_parity_many_to_many_create_data.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/docs_parity_read_connected_data.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/docs_parity_relationship_attributes.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/docs_parity_update_patterns.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/execution_options.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/expression_typing_strict.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/expressions.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/field_sa_type.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/field_validation_alias.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/generics.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/getattr_column_property.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/inheritance.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/joins.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/label.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/many_to_many_relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/mapped_fields.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/model_config.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/model_construct.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/nullable_defaults.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/persisted_helpers.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/relationship_comparators.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/relationship_expressions.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/select_varargs.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/selectinload.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/session_exec.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/session_execute_typed.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/session_get.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/table_dunders.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/table_model_awareness.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/table_via_model_config.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/tuple_expression.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/untyped_fields.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-pydantic-first_ini/relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-pydantic-last_ini/relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict-typed-execute_ini/session_execute_typed.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/aliases.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/annotated_field_metadata.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/constructor.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/docs_parity_many_to_many_create_data.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/docs_parity_relationship_attributes.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/docs_parity_update_patterns.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/generics.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/label.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/many_to_many_relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/mapped_fields.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/model_construct.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/nullable_defaults.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/persisted_helpers.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/relationship_comparators.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/select_varargs.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/table_model_awareness.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/table_via_model_config.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin-strict_ini/tuple_expression.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin_ini/aliases.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin_ini/constructor.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin_ini/expressions.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin_ini/inheritance.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin_ini/label.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin_ini/many_to_many_relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin_ini/model_construct.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin_ini/nullable_defaults.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin_ini/relationship_comparators.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin_ini/relationship_expressions.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin_ini/relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin_ini/select_varargs.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin_ini/table_model_awareness.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/mypy-plugin_ini/untyped_fields.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/outputs/pyproject-plugin_toml/constructor.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/test_mypy.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/smoke_test.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/test_persisted_helpers.py +0 -0
- {sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/test_plugin_unit.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlmodel-mypy-plugin
|
|
3
|
-
Version: 0.19.
|
|
3
|
+
Version: 0.19.1
|
|
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
|
|
@@ -52,8 +52,6 @@ Mypy plugin that improves type checking for [`SQLModel`](https://github.com/fast
|
|
|
52
52
|
|
|
53
53
|
Early/experimental. Implemented scope (supported today):
|
|
54
54
|
|
|
55
|
-
Planned work is tracked in [`ROADMAP.md`](ROADMAP.md).
|
|
56
|
-
|
|
57
55
|
- Generate correct `__init__` / `model_construct` signatures for SQLModel models:
|
|
58
56
|
- treat `sqlmodel.Field(...)` required/optional correctly
|
|
59
57
|
- accept `sqlmodel.Relationship(...)` kwargs for `table=True` models
|
|
@@ -6,8 +6,6 @@ Mypy plugin that improves type checking for [`SQLModel`](https://github.com/fast
|
|
|
6
6
|
|
|
7
7
|
Early/experimental. Implemented scope (supported today):
|
|
8
8
|
|
|
9
|
-
Planned work is tracked in [`ROADMAP.md`](ROADMAP.md).
|
|
10
|
-
|
|
11
9
|
- Generate correct `__init__` / `model_construct` signatures for SQLModel models:
|
|
12
10
|
- treat `sqlmodel.Field(...)` required/optional correctly
|
|
13
11
|
- accept `sqlmodel.Relationship(...)` kwargs for `table=True` models
|
|
@@ -171,7 +171,7 @@ SQLALCHEMY_INSPECT_FULLNAMES = {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
# Increment when plugin changes should invalidate mypy cache.
|
|
174
|
-
__version__ =
|
|
174
|
+
__version__ = 26
|
|
175
175
|
|
|
176
176
|
|
|
177
177
|
class _CollectedField(NamedTuple):
|
|
@@ -1275,34 +1275,6 @@ class SQLModelMypyPlugin(Plugin):
|
|
|
1275
1275
|
return any(_contains_instance_fullname(item, fullname) for item in proper.items)
|
|
1276
1276
|
return False
|
|
1277
1277
|
|
|
1278
|
-
# Accept SQLAlchemy `Select[...]` in `Session.exec(...)`.
|
|
1279
|
-
#
|
|
1280
|
-
# SQLModel defines its own `Select[...]` wrapper class and types `Session.exec` to accept it,
|
|
1281
|
-
# but users often import and use SQLAlchemy's `select()` directly. Runtime supports it, and
|
|
1282
|
-
# with this adjustment we preserve a correctly-shaped `TupleResult[...]` return type.
|
|
1283
|
-
sqlmodel_select_info = _lookup_typeinfo(self, SQLMODEL_SELECT_CLS_FULLNAME)
|
|
1284
|
-
sqlalchemy_select_info = _lookup_typeinfo(self, SQLALCHEMY_SELECT_FULLNAME)
|
|
1285
|
-
if sqlmodel_select_info is not None and sqlalchemy_select_info is not None:
|
|
1286
|
-
if _contains_instance_fullname(default.arg_types[0], sqlmodel_select_info.fullname):
|
|
1287
|
-
# Idempotency: if upstream stubs already accept SQLAlchemy Select, keep unchanged.
|
|
1288
|
-
if not _contains_instance_fullname(
|
|
1289
|
-
default.arg_types[0], sqlalchemy_select_info.fullname
|
|
1290
|
-
):
|
|
1291
|
-
proper_arg0 = get_proper_type(default.arg_types[0])
|
|
1292
|
-
if isinstance(proper_arg0, Instance) and proper_arg0.args:
|
|
1293
|
-
row_type = proper_arg0.args[0]
|
|
1294
|
-
else:
|
|
1295
|
-
row_type = _plugin_any()
|
|
1296
|
-
sqlalchemy_select = Instance(
|
|
1297
|
-
sqlalchemy_select_info,
|
|
1298
|
-
[row_type] * len(sqlalchemy_select_info.defn.type_vars),
|
|
1299
|
-
)
|
|
1300
|
-
new_arg_types = list(default.arg_types)
|
|
1301
|
-
new_arg_types[0] = UnionType.make_union(
|
|
1302
|
-
[default.arg_types[0], sqlalchemy_select]
|
|
1303
|
-
)
|
|
1304
|
-
return default.copy_modified(arg_types=new_arg_types)
|
|
1305
|
-
|
|
1306
1278
|
# Only adjust the broad overload; keep Select/SelectOfScalar overloads precise.
|
|
1307
1279
|
broad = False
|
|
1308
1280
|
if sqlmodel_executable_info is not None and _contains_instance_fullname(
|
|
@@ -1198,11 +1198,25 @@ def add_method(
|
|
|
1198
1198
|
"""
|
|
1199
1199
|
info = cls.info
|
|
1200
1200
|
|
|
1201
|
-
|
|
1201
|
+
def _remove_stmt(stmt: Statement) -> None:
|
|
1202
|
+
# `list.remove(...)` raises ValueError if not present; ignore.
|
|
1203
|
+
try:
|
|
1204
|
+
cls.defs.body.remove(stmt)
|
|
1205
|
+
except ValueError:
|
|
1206
|
+
return
|
|
1207
|
+
|
|
1208
|
+
# Remove previously generated methods (handle both FuncDef and Decorator nodes).
|
|
1202
1209
|
if name in info.names:
|
|
1203
1210
|
sym = info.names[name]
|
|
1204
|
-
if sym.plugin_generated
|
|
1205
|
-
|
|
1211
|
+
if sym.plugin_generated:
|
|
1212
|
+
node = sym.node
|
|
1213
|
+
if isinstance(node, Decorator):
|
|
1214
|
+
# NOTE: older buggy versions appended the underlying FuncDef instead of the
|
|
1215
|
+
# Decorator; remove both to be safe.
|
|
1216
|
+
_remove_stmt(node)
|
|
1217
|
+
_remove_stmt(node.func)
|
|
1218
|
+
elif isinstance(node, FuncDef):
|
|
1219
|
+
_remove_stmt(node)
|
|
1206
1220
|
|
|
1207
1221
|
# Keep existing definition for semantic analysis by renaming it.
|
|
1208
1222
|
if name in info.names:
|
|
@@ -1248,9 +1262,13 @@ def add_method(
|
|
|
1248
1262
|
dec = Decorator(func, [NameExpr("classmethod")], v)
|
|
1249
1263
|
dec.line = info.line
|
|
1250
1264
|
sym = SymbolTableNode(MDEF, dec)
|
|
1265
|
+
# NOTE: keep the class body node as a plain FuncDef (this matches the
|
|
1266
|
+
# previous plugin behavior and avoids override-check churn).
|
|
1267
|
+
body_node: Statement = func
|
|
1251
1268
|
else:
|
|
1252
1269
|
sym = SymbolTableNode(MDEF, func)
|
|
1270
|
+
body_node = func
|
|
1253
1271
|
|
|
1254
1272
|
sym.plugin_generated = True
|
|
1255
1273
|
info.names[name] = sym
|
|
1256
|
-
info.defn.defs.body.append(
|
|
1274
|
+
info.defn.defs.body.append(body_node)
|
|
@@ -28,5 +28,6 @@ with Session(engine) as session:
|
|
|
28
28
|
ok_team: Team | None = team
|
|
29
29
|
|
|
30
30
|
bad_team: Team = team
|
|
31
|
+
# MYPY: error: Incompatible types in assignment (expression has type "Team | None", variable has type "Team") [assignment]
|
|
31
32
|
if team is not None:
|
|
32
33
|
ok_team_name: str = team.name
|
|
@@ -37,7 +37,6 @@ with Session(engine) as session:
|
|
|
37
37
|
stmt_heroes_only = select(Hero).join(Team).where(Team.name == "Preventers")
|
|
38
38
|
for hero3 in session.exec(stmt_heroes_only):
|
|
39
39
|
ok_hero3: Hero = hero3
|
|
40
|
-
# MYPY: error: Incompatible types in assignment (expression has type "tuple[Hero]", variable has type "Hero") [assignment]
|
|
41
40
|
|
|
42
41
|
# Docs pattern: LEFT OUTER join via isouter=True.
|
|
43
42
|
stmt_outer = select(Hero, Team).join(Team, isouter=True)
|
|
@@ -47,3 +46,4 @@ with Session(engine) as session:
|
|
|
47
46
|
|
|
48
47
|
# Outer join may yield `None` for the right-hand entity.
|
|
49
48
|
bad_team4: Team = team4
|
|
49
|
+
# MYPY: error: Incompatible types in assignment (expression has type "Team | None", variable has type "Team") [assignment]
|
|
@@ -32,6 +32,7 @@ with Session(engine) as session:
|
|
|
32
32
|
|
|
33
33
|
# Outer join may yield `None` for the right-hand entity.
|
|
34
34
|
bad_team: Team = team2
|
|
35
|
+
# MYPY: error: Incompatible types in assignment (expression has type "Team | None", variable has type "Team") [assignment]
|
|
35
36
|
if team2 is not None:
|
|
36
37
|
ok_team_name2: str = team2.name
|
|
37
38
|
|
|
@@ -41,6 +42,7 @@ with Session(engine) as session:
|
|
|
41
42
|
ok_hero3: Hero = hero3
|
|
42
43
|
ok_team3: Team | None = team3
|
|
43
44
|
bad_team3: Team = team3
|
|
45
|
+
# MYPY: error: Incompatible types in assignment (expression has type "Team | None", variable has type "Team") [assignment]
|
|
44
46
|
|
|
45
47
|
# One-to-many join via relationship attribute target should also propagate `None`.
|
|
46
48
|
stmt_outer_rel_many = select(Team, Hero).join(Team.heroes, isouter=True)
|
|
@@ -48,5 +50,6 @@ with Session(engine) as session:
|
|
|
48
50
|
ok_team4: Team = team4
|
|
49
51
|
ok_hero4: Hero | None = hero4
|
|
50
52
|
bad_hero4: Hero = hero4
|
|
53
|
+
# MYPY: error: Incompatible types in assignment (expression has type "Hero | None", variable has type "Hero") [assignment]
|
|
51
54
|
if hero4 is not None:
|
|
52
55
|
ok_hero4_name: str = hero4.name
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from sqlalchemy import select, text
|
|
6
|
+
from sqlalchemy.engine.cursor import CursorResult
|
|
7
|
+
from sqlalchemy.engine.result import Result, TupleResult
|
|
8
|
+
from sqlmodel import Field, Session, SQLModel, create_engine, delete
|
|
9
|
+
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Hero(SQLModel, table=True):
|
|
13
|
+
id: int | None = Field(default=None, primary_key=True)
|
|
14
|
+
name: str = Field()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
engine = create_engine("sqlite://")
|
|
18
|
+
|
|
19
|
+
with Session(engine) as session:
|
|
20
|
+
# accept SQLAlchemy (non-SQLModel) Executable statements, e.g. text(...)
|
|
21
|
+
result = session.exec(text("SELECT 1"))
|
|
22
|
+
ok_result: Result[Any] = result
|
|
23
|
+
|
|
24
|
+
# accept SQLAlchemy `select(...)` statements (commonly used with SQLModel)
|
|
25
|
+
select_result = session.exec(select(Hero))
|
|
26
|
+
_ok_select_result: TupleResult[tuple[Hero]] = select_result
|
|
27
|
+
# MYPY: error: Incompatible types in assignment (expression has type "CursorResult[Any]", variable has type "TupleResult[tuple[Hero]]") [assignment]
|
|
28
|
+
|
|
29
|
+
# Keep update/delete statements working (select correct overload).
|
|
30
|
+
delete_result = session.exec(delete(Hero))
|
|
31
|
+
ok_delete_result: CursorResult[Any] = delete_result
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def async_exec(session: AsyncSession) -> None:
|
|
35
|
+
result2 = await session.exec(text("SELECT 1"))
|
|
36
|
+
_ok_result2: Result[Any] = result2
|
|
37
|
+
|
|
38
|
+
select_result2 = await session.exec(select(Hero))
|
|
39
|
+
_ok_select_result2: TupleResult[tuple[Hero]] = select_result2
|
|
40
|
+
# MYPY: error: Incompatible types in assignment (expression has type "CursorResult[Any]", variable has type "TupleResult[tuple[Hero]]") [assignment]
|
|
41
|
+
|
|
42
|
+
delete_result2 = await session.exec(delete(Hero))
|
|
43
|
+
_ok_delete_result2: CursorResult[Any] = delete_result2
|
|
@@ -28,5 +28,6 @@ with Session(engine) as session:
|
|
|
28
28
|
ok_team: Team | None = team
|
|
29
29
|
|
|
30
30
|
bad_team: Team = team
|
|
31
|
+
# MYPY: error: Incompatible types in assignment (expression has type "Team | None", variable has type "Team") [assignment]
|
|
31
32
|
if team is not None:
|
|
32
33
|
ok_team_name: str = team.name
|
|
@@ -32,6 +32,7 @@ with Session(engine) as session:
|
|
|
32
32
|
|
|
33
33
|
# Outer join may yield `None` for the right-hand entity.
|
|
34
34
|
bad_team: Team = team2
|
|
35
|
+
# MYPY: error: Incompatible types in assignment (expression has type "Team | None", variable has type "Team") [assignment]
|
|
35
36
|
if team2 is not None:
|
|
36
37
|
ok_team_name2: str = team2.name
|
|
37
38
|
|
|
@@ -41,6 +42,7 @@ with Session(engine) as session:
|
|
|
41
42
|
ok_hero3: Hero = hero3
|
|
42
43
|
ok_team3: Team | None = team3
|
|
43
44
|
bad_team3: Team = team3
|
|
45
|
+
# MYPY: error: Incompatible types in assignment (expression has type "Team | None", variable has type "Team") [assignment]
|
|
44
46
|
|
|
45
47
|
# One-to-many join via relationship attribute target should also propagate `None`.
|
|
46
48
|
stmt_outer_rel_many = select(Team, Hero).join(Team.heroes, isouter=True)
|
|
@@ -48,5 +50,6 @@ with Session(engine) as session:
|
|
|
48
50
|
ok_team4: Team = team4
|
|
49
51
|
ok_hero4: Hero | None = hero4
|
|
50
52
|
bad_hero4: Hero = hero4
|
|
53
|
+
# MYPY: error: Incompatible types in assignment (expression has type "Hero | None", variable has type "Hero") [assignment]
|
|
51
54
|
if hero4 is not None:
|
|
52
55
|
ok_hero4_name: str = hero4.name
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from sqlalchemy import select, text
|
|
6
|
+
from sqlalchemy.engine.cursor import CursorResult
|
|
7
|
+
from sqlalchemy.engine.result import Result, TupleResult
|
|
8
|
+
from sqlmodel import Field, Session, SQLModel, create_engine, delete
|
|
9
|
+
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Hero(SQLModel, table=True):
|
|
13
|
+
id: int | None = Field(default=None, primary_key=True)
|
|
14
|
+
name: str = Field()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
engine = create_engine("sqlite://")
|
|
18
|
+
|
|
19
|
+
with Session(engine) as session:
|
|
20
|
+
# accept SQLAlchemy (non-SQLModel) Executable statements, e.g. text(...)
|
|
21
|
+
result = session.exec(text("SELECT 1"))
|
|
22
|
+
ok_result: Result[Any] = result
|
|
23
|
+
|
|
24
|
+
# accept SQLAlchemy `select(...)` statements (commonly used with SQLModel)
|
|
25
|
+
select_result = session.exec(select(Hero))
|
|
26
|
+
_ok_select_result: TupleResult[tuple[Hero]] = select_result
|
|
27
|
+
# MYPY: error: Incompatible types in assignment (expression has type "CursorResult[Any]", variable has type "TupleResult[tuple[Hero]]") [assignment]
|
|
28
|
+
|
|
29
|
+
# Keep update/delete statements working (select correct overload).
|
|
30
|
+
delete_result = session.exec(delete(Hero))
|
|
31
|
+
ok_delete_result: CursorResult[Any] = delete_result
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def async_exec(session: AsyncSession) -> None:
|
|
35
|
+
result2 = await session.exec(text("SELECT 1"))
|
|
36
|
+
_ok_result2: Result[Any] = result2
|
|
37
|
+
|
|
38
|
+
select_result2 = await session.exec(select(Hero))
|
|
39
|
+
_ok_select_result2: TupleResult[tuple[Hero]] = select_result2
|
|
40
|
+
# MYPY: error: Incompatible types in assignment (expression has type "CursorResult[Any]", variable has type "TupleResult[tuple[Hero]]") [assignment]
|
|
41
|
+
|
|
42
|
+
delete_result2 = await session.exec(delete(Hero))
|
|
43
|
+
_ok_delete_result2: CursorResult[Any] = delete_result2
|
|
@@ -657,7 +657,58 @@ def test_add_method_generates_and_replaces_methods() -> None:
|
|
|
657
657
|
api, cls, "cm", args=[arg], return_type=AnyType(TypeOfAny.explicit), is_classmethod=True
|
|
658
658
|
)
|
|
659
659
|
assert "cm" in cls.info.names
|
|
660
|
-
|
|
660
|
+
cm_node = cls.info.names["cm"].node
|
|
661
|
+
assert isinstance(cm_node, Decorator)
|
|
662
|
+
assert cm_node.func.is_class is True
|
|
663
|
+
assert cm_node not in cls.defs.body
|
|
664
|
+
assert cm_node.func in cls.defs.body
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
def test_add_method_classmethod_reentry_is_idempotent_and_handles_legacy_body() -> None:
|
|
668
|
+
api = DummyAPI()
|
|
669
|
+
cls = make_sqlmodel_class()
|
|
670
|
+
|
|
671
|
+
any_t = AnyType(TypeOfAny.explicit)
|
|
672
|
+
arg = Argument(Var("x", any_t), any_t, None, ARG_NAMED)
|
|
673
|
+
|
|
674
|
+
# First generation
|
|
675
|
+
add_method(
|
|
676
|
+
api, cls, "cm", args=[arg], return_type=AnyType(TypeOfAny.explicit), is_classmethod=True
|
|
677
|
+
)
|
|
678
|
+
node1 = cls.info.names["cm"].node
|
|
679
|
+
assert isinstance(node1, Decorator)
|
|
680
|
+
assert node1 not in cls.defs.body
|
|
681
|
+
assert node1.func in cls.defs.body
|
|
682
|
+
assert node1.func.is_class is True
|
|
683
|
+
|
|
684
|
+
# Re-entry: previously generated Decorator should be removed, no duplicates.
|
|
685
|
+
add_method(
|
|
686
|
+
api, cls, "cm", args=[arg], return_type=AnyType(TypeOfAny.explicit), is_classmethod=True
|
|
687
|
+
)
|
|
688
|
+
node2 = cls.info.names["cm"].node
|
|
689
|
+
assert isinstance(node2, Decorator)
|
|
690
|
+
assert node2 not in cls.defs.body
|
|
691
|
+
assert node2.func in cls.defs.body
|
|
692
|
+
assert node1.func not in cls.defs.body
|
|
693
|
+
assert node2.func.is_class is True
|
|
694
|
+
assert sum(1 for s in cls.defs.body if isinstance(s, FuncDef) and s.name == "cm") == 1
|
|
695
|
+
|
|
696
|
+
# Simulate legacy buggy state: body contains both Decorator and FuncDef.
|
|
697
|
+
cls.defs.body.append(node2)
|
|
698
|
+
assert node2 in cls.defs.body
|
|
699
|
+
assert node2.func in cls.defs.body
|
|
700
|
+
|
|
701
|
+
add_method(
|
|
702
|
+
api, cls, "cm", args=[arg], return_type=AnyType(TypeOfAny.explicit), is_classmethod=True
|
|
703
|
+
)
|
|
704
|
+
node3 = cls.info.names["cm"].node
|
|
705
|
+
assert isinstance(node3, Decorator)
|
|
706
|
+
assert node3 not in cls.defs.body
|
|
707
|
+
assert node3.func in cls.defs.body
|
|
708
|
+
assert node2 not in cls.defs.body
|
|
709
|
+
assert node2.func not in cls.defs.body
|
|
710
|
+
assert node3.func.is_class is True
|
|
711
|
+
assert sum(1 for s in cls.defs.body if isinstance(s, FuncDef) and s.name == "cm") == 1
|
|
661
712
|
|
|
662
713
|
|
|
663
714
|
def test_transform_defer_paths() -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/configs/mypy-plugin.ini
RENAMED
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/configs/pyproject-plugin.toml
RENAMED
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/aliased_joins.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/constructor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/execution_options.py
RENAMED
|
File without changes
|
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/expressions.py
RENAMED
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/field_sa_type.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/inheritance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/mapped_fields.py
RENAMED
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/model_config.py
RENAMED
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/model_construct.py
RENAMED
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/nullable_defaults.py
RENAMED
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/persisted_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/relationships.py
RENAMED
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/select_varargs.py
RENAMED
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/selectinload.py
RENAMED
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/session_exec.py
RENAMED
|
File without changes
|
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/session_get.py
RENAMED
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/table_dunders.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/tuple_expression.py
RENAMED
|
File without changes
|
{sqlmodel_mypy_plugin-0.19.0 → sqlmodel_mypy_plugin-0.19.1}/tests/mypy/modules/untyped_fields.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|