sqlmodel-mypy-plugin 0.20.0__tar.gz → 0.21.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.
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/PKG-INFO +1 -1
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/pyproject.toml +4 -1
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/src/sqlmodel_mypy/plugin.py +33 -13
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/src/sqlmodel_mypy/transform.py +3 -2
- sqlmodel_mypy_plugin-0.21.2/tests/mypy/configs/mypy-plugin-recursive.ini +10 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/execution_options.py +1 -2
- sqlmodel_mypy_plugin-0.21.2/tests/mypy/modules/recursive_aliases.py +35 -0
- sqlmodel_mypy_plugin-0.21.2/tests/mypy/modules/recursive_aliases_pep695.py +26 -0
- sqlmodel_mypy_plugin-0.21.2/tests/mypy/modules/row_generics.py +41 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/select_varargs.py +1 -2
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/session_execute_typed.py +12 -2
- sqlmodel_mypy_plugin-0.21.2/tests/mypy/outputs/mypy-plugin-recursive_ini/recursive_aliases_pep695.py +30 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict-typed-execute_ini/session_execute_typed.py +12 -2
- {sqlmodel_mypy_plugin-0.20.0/tests/mypy/outputs/mypy-plugin_ini → sqlmodel_mypy_plugin-0.21.2/tests/mypy/outputs/mypy-plugin-strict_ini}/execution_options.py +1 -2
- sqlmodel_mypy_plugin-0.21.2/tests/mypy/outputs/mypy-plugin-strict_ini/recursive_aliases.py +39 -0
- sqlmodel_mypy_plugin-0.21.2/tests/mypy/outputs/mypy-plugin-strict_ini/select_varargs.py +35 -0
- {sqlmodel_mypy_plugin-0.20.0/tests/mypy/outputs/mypy-plugin-strict_ini → sqlmodel_mypy_plugin-0.21.2/tests/mypy/outputs/mypy-plugin_ini}/execution_options.py +1 -2
- sqlmodel_mypy_plugin-0.21.2/tests/mypy/outputs/mypy-plugin_ini/select_varargs.py +35 -0
- sqlmodel_mypy_plugin-0.21.2/tests/mypy/outputs/sqlalchemy-2.1/mypy-plugin-strict-typed-execute_ini/session_execute_typed.py +48 -0
- sqlmodel_mypy_plugin-0.21.2/tests/mypy/outputs/sqlalchemy-2.1/mypy-plugin-strict_ini/row_generics.py +47 -0
- {sqlmodel_mypy_plugin-0.20.0/tests/mypy/outputs/mypy-plugin_ini → sqlmodel_mypy_plugin-0.21.2/tests/mypy/outputs/sqlalchemy-2.1/mypy-plugin-strict_ini}/select_varargs.py +2 -3
- sqlmodel_mypy_plugin-0.21.2/tests/mypy/outputs/sqlalchemy-2.1/mypy-plugin-strict_ini/session_exec.py +43 -0
- {sqlmodel_mypy_plugin-0.20.0/tests/mypy/outputs/mypy-plugin-strict_ini → sqlmodel_mypy_plugin-0.21.2/tests/mypy/outputs/sqlalchemy-2.1/mypy-plugin_ini}/select_varargs.py +2 -3
- sqlmodel_mypy_plugin-0.21.2/tests/mypy/outputs/sqlalchemy-2.1/mypy-plugin_ini/session_exec.py +43 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/test_mypy.py +36 -5
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/test_plugin_unit.py +37 -10
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/test_transform_unit.py +33 -1
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/.gitignore +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/.pre-commit-config.yaml +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/AGENTS.md +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/LICENSE +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/README.md +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/src/sqlmodel_mypy/__init__.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/src/sqlmodel_mypy/persisted.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/src/sqlmodel_mypy/py.typed +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/conftest.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/configs/mypy-plugin-no-untyped.ini +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/configs/mypy-plugin-pydantic-first.ini +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/configs/mypy-plugin-pydantic-last.ini +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/configs/mypy-plugin-strict-no-explicit-any.ini +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/configs/mypy-plugin-strict-typed-execute.ini +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/configs/mypy-plugin-strict.ini +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/configs/mypy-plugin.ini +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/configs/pyproject-plugin.toml +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/aliased_joins.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/aliases.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/annotated_field_metadata.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/constructor.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/docs_parity_many_to_many_create_data.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/docs_parity_read_connected_data.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/docs_parity_relationship_attributes.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/docs_parity_update_patterns.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/expression_typing_strict.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/expressions.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/field_sa_type.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/field_validation_alias.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/generics.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/getattr_column_property.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/inheritance.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/joins.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/label.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/many_to_many_relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/mapped_fields.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/model_config.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/model_construct.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/nullable_defaults.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/persisted_helpers.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/relationship_comparators.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/relationship_expressions.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/selectinload.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/sequence_name_collision.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/session_exec.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/session_get.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/sqlmodel_reexported_sequence.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/table_dunders.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/table_model_awareness.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/table_via_model_config.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/tuple_expression.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/untyped_fields.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-pydantic-first_ini/relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-pydantic-last_ini/relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/aliased_joins.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/aliases.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/annotated_field_metadata.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/constructor.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/docs_parity_many_to_many_create_data.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/docs_parity_read_connected_data.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/docs_parity_relationship_attributes.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/docs_parity_update_patterns.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/generics.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/joins.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/label.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/many_to_many_relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/mapped_fields.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/model_construct.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/nullable_defaults.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/persisted_helpers.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/relationship_comparators.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/sequence_name_collision.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/session_exec.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/sqlmodel_reexported_sequence.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/table_model_awareness.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/table_via_model_config.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin-strict_ini/tuple_expression.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/aliased_joins.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/aliases.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/constructor.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/expressions.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/inheritance.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/joins.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/label.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/many_to_many_relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/model_construct.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/nullable_defaults.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/relationship_comparators.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/relationship_expressions.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/sequence_name_collision.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/session_exec.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/sqlmodel_reexported_sequence.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/table_model_awareness.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/mypy-plugin_ini/untyped_fields.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/outputs/pyproject-plugin_toml/constructor.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/smoke_test.py +0 -0
- {sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/test_persisted_helpers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sqlmodel-mypy-plugin
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.21.2
|
|
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
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sqlmodel-mypy-plugin"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.21.2"
|
|
8
8
|
description = "Mypy plugin for SQLModel (better constructor/Field typing)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -64,6 +64,9 @@ line-length = 100
|
|
|
64
64
|
target-version = "py310"
|
|
65
65
|
exclude = ["tests/mypy/outputs"]
|
|
66
66
|
|
|
67
|
+
[tool.ruff.per-file-target-version]
|
|
68
|
+
"tests/mypy/modules/recursive_aliases_pep695.py" = "py312"
|
|
69
|
+
|
|
67
70
|
[tool.ruff.lint]
|
|
68
71
|
select = [
|
|
69
72
|
"E", # pycodestyle errors
|
|
@@ -58,6 +58,7 @@ from mypy.types import (
|
|
|
58
58
|
TypeOfAny,
|
|
59
59
|
TypeType,
|
|
60
60
|
UnionType,
|
|
61
|
+
UnpackType,
|
|
61
62
|
get_proper_type,
|
|
62
63
|
)
|
|
63
64
|
from mypy.typevars import fill_typevars
|
|
@@ -171,7 +172,7 @@ SQLALCHEMY_INSPECT_FULLNAMES = {
|
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
# Increment when plugin changes should invalidate mypy cache.
|
|
174
|
-
__version__ =
|
|
175
|
+
__version__ = 29
|
|
175
176
|
|
|
176
177
|
|
|
177
178
|
class _CollectedField(NamedTuple):
|
|
@@ -926,7 +927,7 @@ class SQLModelMypyPlugin(Plugin):
|
|
|
926
927
|
|
|
927
928
|
SQLModel's generated stubs traditionally cap `select()` overloads at 4 entities.
|
|
928
929
|
For 5+ entities, fall back to a conservative signature that accepts the call and
|
|
929
|
-
returns
|
|
930
|
+
returns a Select with one unknown value per selected entity.
|
|
930
931
|
"""
|
|
931
932
|
call = ctx.context
|
|
932
933
|
if not isinstance(call, CallExpr):
|
|
@@ -977,7 +978,7 @@ class SQLModelMypyPlugin(Plugin):
|
|
|
977
978
|
any_t = _plugin_any()
|
|
978
979
|
tuple_fallback = ctx.api.named_generic_type("builtins.tuple", [any_t])
|
|
979
980
|
row_type = TupleType([any_t] * positional_count, tuple_fallback)
|
|
980
|
-
ret_type =
|
|
981
|
+
ret_type = self._instance_with_row_type(select_info, row_type)
|
|
981
982
|
|
|
982
983
|
fallback = default_items[0].fallback if default_items else tuple_fallback
|
|
983
984
|
arg_types = [any_t] * positional_count
|
|
@@ -990,7 +991,7 @@ class SQLModelMypyPlugin(Plugin):
|
|
|
990
991
|
|
|
991
992
|
Our signature hook for 5+ args deliberately uses `Any` to avoid "no overload matches"
|
|
992
993
|
errors. Here, once mypy has inferred argument expression types, we recover a more precise
|
|
993
|
-
|
|
994
|
+
row element types for the common cases (models + column expressions).
|
|
994
995
|
"""
|
|
995
996
|
call = ctx.context
|
|
996
997
|
if not isinstance(call, CallExpr):
|
|
@@ -1012,7 +1013,7 @@ class SQLModelMypyPlugin(Plugin):
|
|
|
1012
1013
|
proper_default = get_proper_type(default)
|
|
1013
1014
|
if not isinstance(proper_default, Instance) or not proper_default.args:
|
|
1014
1015
|
return default
|
|
1015
|
-
proper_row = get_proper_type(proper_default.
|
|
1016
|
+
proper_row = get_proper_type(self._instance_row_type(proper_default, ctx.api))
|
|
1016
1017
|
if not isinstance(proper_row, TupleType):
|
|
1017
1018
|
return default
|
|
1018
1019
|
|
|
@@ -1036,7 +1037,7 @@ class SQLModelMypyPlugin(Plugin):
|
|
|
1036
1037
|
any_t = _plugin_any()
|
|
1037
1038
|
tuple_fallback = ctx.api.named_generic_type("builtins.tuple", [any_t])
|
|
1038
1039
|
row_type = TupleType(items, tuple_fallback)
|
|
1039
|
-
return
|
|
1040
|
+
return self._instance_with_row_type(proper_default.type, row_type)
|
|
1040
1041
|
|
|
1041
1042
|
def _sqlmodel_select_entity_result_type(self, typ: Type) -> Type:
|
|
1042
1043
|
"""Return the value type produced by an entity passed to `select(...)`.
|
|
@@ -1054,6 +1055,9 @@ class SQLModelMypyPlugin(Plugin):
|
|
|
1054
1055
|
return UnionType.make_union(
|
|
1055
1056
|
[self._sqlmodel_select_entity_result_type(item) for item in proper.items]
|
|
1056
1057
|
)
|
|
1058
|
+
if isinstance(proper, FunctionLike) and proper.is_type_obj():
|
|
1059
|
+
# Mypy represents a model class expression by its constructor overloads.
|
|
1060
|
+
return UnionType.make_union([item.ret_type for item in proper.items])
|
|
1057
1061
|
if isinstance(proper, TypeType):
|
|
1058
1062
|
item = get_proper_type(proper.item)
|
|
1059
1063
|
if isinstance(item, Instance):
|
|
@@ -1371,6 +1375,23 @@ class SQLModelMypyPlugin(Plugin):
|
|
|
1371
1375
|
coro = _named_generic_type_or_none(ctx.api, "typing.Coroutine", [any_t, any_t, result_type])
|
|
1372
1376
|
return coro or default
|
|
1373
1377
|
|
|
1378
|
+
@staticmethod
|
|
1379
|
+
def _instance_row_type(instance: Instance, api: Any) -> Type:
|
|
1380
|
+
"""Read a row from SQLAlchemy 2.0's tuple or 2.1's variadic arguments."""
|
|
1381
|
+
if instance.type.has_type_var_tuple_type:
|
|
1382
|
+
tuple_fallback = api.named_generic_type("builtins.tuple", [_plugin_any()])
|
|
1383
|
+
return TupleType(list(instance.args), tuple_fallback)
|
|
1384
|
+
return instance.args[0]
|
|
1385
|
+
|
|
1386
|
+
@staticmethod
|
|
1387
|
+
def _instance_with_row_type(info: TypeInfo, row_type: Type) -> Instance:
|
|
1388
|
+
"""Use the installed class's generic shape, without guessing package versions."""
|
|
1389
|
+
if info.has_type_var_tuple_type:
|
|
1390
|
+
proper_row = get_proper_type(row_type)
|
|
1391
|
+
args = proper_row.items if isinstance(proper_row, TupleType) else [UnpackType(row_type)]
|
|
1392
|
+
return Instance(info, args)
|
|
1393
|
+
return Instance(info, [row_type])
|
|
1394
|
+
|
|
1374
1395
|
def _row_tuple_type_from_typed_statement(self, statement: Type, api: Any) -> Type | None:
|
|
1375
1396
|
"""Infer the row tuple type produced by a SQL statement (best-effort)."""
|
|
1376
1397
|
proper = get_proper_type(statement)
|
|
@@ -1385,12 +1406,12 @@ class SQLModelMypyPlugin(Plugin):
|
|
|
1385
1406
|
tuple_fallback = api.named_generic_type("builtins.tuple", [any_t])
|
|
1386
1407
|
return TupleType([scalar_t], tuple_fallback)
|
|
1387
1408
|
|
|
1388
|
-
#
|
|
1409
|
+
# SQLAlchemy 2.1 and SQLModel 0.0.48 use variadic row parameters.
|
|
1389
1410
|
if proper.type.fullname in {
|
|
1390
1411
|
SQLMODEL_SELECT_CLS_FULLNAME,
|
|
1391
1412
|
SQLALCHEMY_SELECT_FULLNAME,
|
|
1392
1413
|
} or proper.type.has_base(SQLALCHEMY_SELECT_FULLNAME):
|
|
1393
|
-
return proper
|
|
1414
|
+
return self._instance_row_type(proper, api)
|
|
1394
1415
|
|
|
1395
1416
|
return None
|
|
1396
1417
|
|
|
@@ -1399,11 +1420,10 @@ class SQLModelMypyPlugin(Plugin):
|
|
|
1399
1420
|
for fullname in ("sqlalchemy.engine.Result", "sqlalchemy.engine.result.Result"):
|
|
1400
1421
|
inst = _named_generic_type_or_none(api, fullname, [row_tuple_type])
|
|
1401
1422
|
if inst is not None:
|
|
1402
|
-
return inst
|
|
1423
|
+
return self._instance_with_row_type(inst.type, row_tuple_type)
|
|
1403
1424
|
info = _lookup_typeinfo(self, fullname)
|
|
1404
1425
|
if info is not None:
|
|
1405
|
-
|
|
1406
|
-
return Instance(info, args)
|
|
1426
|
+
return self._instance_with_row_type(info, row_tuple_type)
|
|
1407
1427
|
return None
|
|
1408
1428
|
|
|
1409
1429
|
def _sqlalchemy_select_join_return_type_callback(self, ctx: MethodContext) -> Type:
|
|
@@ -1661,7 +1681,7 @@ class SQLModelMypyPlugin(Plugin):
|
|
|
1661
1681
|
if not receiver.args:
|
|
1662
1682
|
return ctx.default_return_type
|
|
1663
1683
|
|
|
1664
|
-
tp = get_proper_type(receiver.
|
|
1684
|
+
tp = get_proper_type(self._instance_row_type(receiver, ctx.api))
|
|
1665
1685
|
if not isinstance(tp, TupleType):
|
|
1666
1686
|
return ctx.default_return_type
|
|
1667
1687
|
|
|
@@ -1682,7 +1702,7 @@ class SQLModelMypyPlugin(Plugin):
|
|
|
1682
1702
|
return ctx.default_return_type
|
|
1683
1703
|
|
|
1684
1704
|
new_tp = tp.copy_modified(items=new_items)
|
|
1685
|
-
return
|
|
1705
|
+
return self._instance_with_row_type(receiver.type, new_tp)
|
|
1686
1706
|
|
|
1687
1707
|
def _sqlmodel_class_attr_type_callback(self, ctx: AttributeContext) -> Type:
|
|
1688
1708
|
if ctx.is_lvalue:
|
|
@@ -448,8 +448,9 @@ class ForceInvariantTypeVars(TypeTranslator):
|
|
|
448
448
|
return modified
|
|
449
449
|
|
|
450
450
|
def visit_type_alias_type(self, t: TypeAliasType) -> Type: # noqa: D102
|
|
451
|
-
#
|
|
452
|
-
|
|
451
|
+
# Alias targets can refer back to themselves. Preserve their graph and
|
|
452
|
+
# translate only the supplied arguments, like mypy's ExpandTypeVisitor.
|
|
453
|
+
return t.copy_modified(args=[arg.accept(self) for arg in t.args])
|
|
453
454
|
|
|
454
455
|
|
|
455
456
|
class SQLModelField:
|
{sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/execution_options.py
RENAMED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
|
4
|
-
from sqlmodel.sql._expression_select_cls import Select as SQLModelSelect
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
class Team(SQLModel, table=True):
|
|
@@ -21,7 +20,7 @@ stmt = select(Hero, Team).join(Team, isouter=True).execution_options(populate_ex
|
|
|
21
20
|
|
|
22
21
|
# `execution_options()` should preserve the `Select[...]` generic, including outer-join `None`
|
|
23
22
|
# propagation for the joined entity.
|
|
24
|
-
ok_stmt
|
|
23
|
+
ok_stmt = stmt
|
|
25
24
|
|
|
26
25
|
with Session(engine) as session:
|
|
27
26
|
for hero, team in session.exec(stmt):
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Generic, TypeAlias, TypeVar
|
|
4
|
+
|
|
5
|
+
from sqlmodel import SQLModel
|
|
6
|
+
|
|
7
|
+
JsonObject: TypeAlias = list["JsonObject"]
|
|
8
|
+
JsonValue: TypeAlias = str | int | None | list["JsonValue"] | dict[str, "JsonValue"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RecursiveTable(SQLModel, table=True):
|
|
12
|
+
field: JsonObject
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Payload(SQLModel):
|
|
16
|
+
value: JsonValue
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
RecursiveTable(field=[[], [[]]])
|
|
20
|
+
RecursiveTable(field="invalid")
|
|
21
|
+
RecursiveTable.model_construct(field=[[]])
|
|
22
|
+
RecursiveTable.model_construct(field=123)
|
|
23
|
+
Payload(value={"nested": ["text", 1, None]})
|
|
24
|
+
Payload(value=object())
|
|
25
|
+
|
|
26
|
+
T_co = TypeVar("T_co", covariant=True)
|
|
27
|
+
Values: TypeAlias = list[T_co]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Box(SQLModel, Generic[T_co]):
|
|
31
|
+
values: Values[T_co]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
Box[int](values=[1])
|
|
35
|
+
Box[int](values=["invalid"])
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from sqlmodel import SQLModel
|
|
4
|
+
|
|
5
|
+
type JsonObject = list[JsonObject]
|
|
6
|
+
type JsonValue = str | int | None | list[JsonValue] | dict[str, JsonValue]
|
|
7
|
+
type Left = list[Right]
|
|
8
|
+
type Right = dict[str, Left]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RecursiveTable(SQLModel, table=True):
|
|
12
|
+
field: JsonObject
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Payload(SQLModel):
|
|
16
|
+
value: JsonValue
|
|
17
|
+
mutual: Left
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
RecursiveTable(field=[[], [[]]])
|
|
21
|
+
RecursiveTable(field="invalid")
|
|
22
|
+
RecursiveTable.model_construct(field=[[]])
|
|
23
|
+
RecursiveTable.model_construct(field=123)
|
|
24
|
+
Payload(value={"nested": ["text", 1, None]}, mutual=[{"child": []}])
|
|
25
|
+
Payload(value=object(), mutual=[])
|
|
26
|
+
Payload(value=None, mutual=[123])
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from sqlalchemy import select as sa_select
|
|
4
|
+
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Team(SQLModel, table=True):
|
|
8
|
+
id: int | None = Field(default=None, primary_key=True)
|
|
9
|
+
name: str
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Hero(SQLModel, table=True):
|
|
13
|
+
id: int | None = Field(default=None, primary_key=True)
|
|
14
|
+
name: str
|
|
15
|
+
team_id: int | None = Field(default=None, foreign_key="team.id")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
engine = create_engine("sqlite://")
|
|
19
|
+
# The 5+ entity fallback and outer-join nullability must compose.
|
|
20
|
+
statement = select(Hero, Team, Hero.id, Team.id, Hero.name).outerjoin(Team)
|
|
21
|
+
with Session(engine) as session:
|
|
22
|
+
for hero, team, hero_id, _team_id, name in session.exec(statement):
|
|
23
|
+
ok_hero: Hero = hero
|
|
24
|
+
ok_team: Team | None = team
|
|
25
|
+
ok_id: int | None = hero_id
|
|
26
|
+
ok_name: str = name
|
|
27
|
+
bad_team: Team = team
|
|
28
|
+
bad_id: int = hero_id
|
|
29
|
+
bad_name: int = name
|
|
30
|
+
|
|
31
|
+
# A SQLModel select must preserve column arity when executed on a Core connection.
|
|
32
|
+
with engine.connect() as connection:
|
|
33
|
+
for hero_id2, name2, _team_id2, _team_name2, _repeated_id in connection.execute(
|
|
34
|
+
select(Hero.id, Hero.name, Hero.team_id, Team.name, Hero.id)
|
|
35
|
+
):
|
|
36
|
+
ok_name2: str = name2
|
|
37
|
+
bad_id2: int = hero_id2
|
|
38
|
+
bad_name2: int = name2
|
|
39
|
+
for hero3, team3 in connection.execute(sa_select(Hero, Team).outerjoin(Team)):
|
|
40
|
+
ok_hero3: Hero = hero3
|
|
41
|
+
bad_team3: Team = team3
|
{sqlmodel_mypy_plugin-0.20.0 → sqlmodel_mypy_plugin-0.21.2}/tests/mypy/modules/select_varargs.py
RENAMED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
|
4
|
-
from sqlmodel.sql._expression_select_cls import Select as SQLModelSelect
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
class Team(SQLModel, table=True):
|
|
@@ -19,7 +18,7 @@ class Hero(SQLModel, table=True):
|
|
|
19
18
|
stmt = select(Hero, Team, Hero.id, Team.id, Hero.name)
|
|
20
19
|
|
|
21
20
|
# Prefer returning SQLModel's own `Select[...]` wrapper, with best-effort element typing.
|
|
22
|
-
ok_stmt
|
|
21
|
+
ok_stmt = stmt
|
|
23
22
|
|
|
24
23
|
engine = create_engine("sqlite://")
|
|
25
24
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from sqlalchemy.engine.result import Result
|
|
4
3
|
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
|
5
4
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
6
5
|
|
|
@@ -16,7 +15,9 @@ with Session(engine) as session:
|
|
|
16
15
|
result = session.execute(select(Hero))
|
|
17
16
|
|
|
18
17
|
# When `typed_execute=true`, `execute(select(Model))` should not degrade to `Result[Any]`.
|
|
19
|
-
|
|
18
|
+
row = result.one()
|
|
19
|
+
ok_row_hero: Hero = row[0]
|
|
20
|
+
bad_row_bool: bool = row[0]
|
|
20
21
|
|
|
21
22
|
ok_hero: Hero = result.scalars().one()
|
|
22
23
|
bad_bool: bool = result.scalars().one()
|
|
@@ -30,3 +31,12 @@ async def async_execute(session: AsyncSession) -> None:
|
|
|
30
31
|
result3 = await session.execute(select(Hero))
|
|
31
32
|
_ok_hero2: Hero = result3.scalars().one()
|
|
32
33
|
_bad_bool2: bool = result3.scalars().one()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async def async_execute_columns(session: AsyncSession) -> None:
|
|
37
|
+
result = await session.execute(select(Hero.id, Hero.name))
|
|
38
|
+
for hero_id, hero_name in result:
|
|
39
|
+
_ok_id: int | None = hero_id
|
|
40
|
+
_ok_name: str = hero_name
|
|
41
|
+
_bad_id: int = hero_id
|
|
42
|
+
_bad_name: int = hero_name
|
sqlmodel_mypy_plugin-0.21.2/tests/mypy/outputs/mypy-plugin-recursive_ini/recursive_aliases_pep695.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from sqlmodel import SQLModel
|
|
4
|
+
|
|
5
|
+
type JsonObject = list[JsonObject]
|
|
6
|
+
type JsonValue = str | int | None | list[JsonValue] | dict[str, JsonValue]
|
|
7
|
+
type Left = list[Right]
|
|
8
|
+
type Right = dict[str, Left]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RecursiveTable(SQLModel, table=True):
|
|
12
|
+
field: JsonObject
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Payload(SQLModel):
|
|
16
|
+
value: JsonValue
|
|
17
|
+
mutual: Left
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
RecursiveTable(field=[[], [[]]])
|
|
21
|
+
RecursiveTable(field="invalid")
|
|
22
|
+
# MYPY: error: Argument "field" to "__init__" of "RecursiveTable" has incompatible type "str"; expected "JsonObject" [arg-type]
|
|
23
|
+
RecursiveTable.model_construct(field=[[]])
|
|
24
|
+
RecursiveTable.model_construct(field=123)
|
|
25
|
+
# MYPY: error: Argument "field" to "model_construct" of "RecursiveTable" has incompatible type "int"; expected "JsonObject" [arg-type]
|
|
26
|
+
Payload(value={"nested": ["text", 1, None]}, mutual=[{"child": []}])
|
|
27
|
+
Payload(value=object(), mutual=[])
|
|
28
|
+
# MYPY: error: Argument "value" to "__init__" of "Payload" has incompatible type "object"; expected "JsonValue" [arg-type]
|
|
29
|
+
Payload(value=None, mutual=[123])
|
|
30
|
+
# MYPY: error: List item 0 has incompatible type "int"; expected "dict[str, Left]" [list-item]
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from sqlalchemy.engine.result import Result
|
|
4
3
|
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
|
5
4
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
6
5
|
|
|
@@ -16,7 +15,9 @@ with Session(engine) as session:
|
|
|
16
15
|
result = session.execute(select(Hero))
|
|
17
16
|
|
|
18
17
|
# When `typed_execute=true`, `execute(select(Model))` should not degrade to `Result[Any]`.
|
|
19
|
-
|
|
18
|
+
row = result.one()
|
|
19
|
+
ok_row_hero: Hero = row[0]
|
|
20
|
+
bad_row_bool: bool = row[0]
|
|
20
21
|
|
|
21
22
|
ok_hero: Hero = result.scalars().one()
|
|
22
23
|
bad_bool: bool = result.scalars().one()
|
|
@@ -33,3 +34,12 @@ async def async_execute(session: AsyncSession) -> None:
|
|
|
33
34
|
_ok_hero2: Hero = result3.scalars().one()
|
|
34
35
|
_bad_bool2: bool = result3.scalars().one()
|
|
35
36
|
# MYPY: error: Incompatible types in assignment (expression has type "Hero", variable has type "bool") [assignment]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def async_execute_columns(session: AsyncSession) -> None:
|
|
40
|
+
result = await session.execute(select(Hero.id, Hero.name))
|
|
41
|
+
for hero_id, hero_name in result:
|
|
42
|
+
_ok_id: int | None = hero_id
|
|
43
|
+
_ok_name: str = hero_name
|
|
44
|
+
_bad_id: int = hero_id
|
|
45
|
+
_bad_name: int = hero_name
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
|
4
|
-
from sqlmodel.sql._expression_select_cls import Select as SQLModelSelect
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
class Team(SQLModel, table=True):
|
|
@@ -21,7 +20,7 @@ stmt = select(Hero, Team).join(Team, isouter=True).execution_options(populate_ex
|
|
|
21
20
|
|
|
22
21
|
# `execution_options()` should preserve the `Select[...]` generic, including outer-join `None`
|
|
23
22
|
# propagation for the joined entity.
|
|
24
|
-
ok_stmt
|
|
23
|
+
ok_stmt = stmt
|
|
25
24
|
|
|
26
25
|
with Session(engine) as session:
|
|
27
26
|
for hero, team in session.exec(stmt):
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Generic, TypeAlias, TypeVar
|
|
4
|
+
|
|
5
|
+
from sqlmodel import SQLModel
|
|
6
|
+
|
|
7
|
+
JsonObject: TypeAlias = list["JsonObject"]
|
|
8
|
+
JsonValue: TypeAlias = str | int | None | list["JsonValue"] | dict[str, "JsonValue"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RecursiveTable(SQLModel, table=True):
|
|
12
|
+
field: JsonObject
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Payload(SQLModel):
|
|
16
|
+
value: JsonValue
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
RecursiveTable(field=[[], [[]]])
|
|
20
|
+
RecursiveTable(field="invalid")
|
|
21
|
+
# MYPY: error: Argument "field" to "__init__" of "RecursiveTable" has incompatible type "str"; expected "JsonObject" [arg-type]
|
|
22
|
+
RecursiveTable.model_construct(field=[[]])
|
|
23
|
+
RecursiveTable.model_construct(field=123)
|
|
24
|
+
# MYPY: error: Argument "field" to "model_construct" of "RecursiveTable" has incompatible type "int"; expected "JsonObject" [arg-type]
|
|
25
|
+
Payload(value={"nested": ["text", 1, None]})
|
|
26
|
+
Payload(value=object())
|
|
27
|
+
# MYPY: error: Argument "value" to "__init__" of "Payload" has incompatible type "object"; expected "JsonValue" [arg-type]
|
|
28
|
+
|
|
29
|
+
T_co = TypeVar("T_co", covariant=True)
|
|
30
|
+
Values: TypeAlias = list[T_co]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Box(SQLModel, Generic[T_co]):
|
|
34
|
+
values: Values[T_co]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
Box[int](values=[1])
|
|
38
|
+
Box[int](values=["invalid"])
|
|
39
|
+
# MYPY: error: List item 0 has incompatible type "str"; expected "int" [list-item]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Team(SQLModel, table=True):
|
|
7
|
+
id: int | None = Field(default=None, primary_key=True)
|
|
8
|
+
name: str = Field()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Hero(SQLModel, table=True):
|
|
12
|
+
id: int | None = Field(default=None, primary_key=True)
|
|
13
|
+
name: str = Field()
|
|
14
|
+
team_id: int | None = Field(default=None, foreign_key="team.id")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# SQLModel's generated `select()` overloads cap at 4 entities; ensure 5+ works.
|
|
18
|
+
stmt = select(Hero, Team, Hero.id, Team.id, Hero.name)
|
|
19
|
+
|
|
20
|
+
# Prefer returning SQLModel's own `Select[...]` wrapper, with best-effort element typing.
|
|
21
|
+
ok_stmt = stmt
|
|
22
|
+
|
|
23
|
+
engine = create_engine("sqlite://")
|
|
24
|
+
|
|
25
|
+
with Session(engine) as session:
|
|
26
|
+
for hero, team, hero_id, team_id, hero_name in session.exec(stmt):
|
|
27
|
+
ok_hero: Hero = hero
|
|
28
|
+
ok_team: Team = team
|
|
29
|
+
ok_team_id: int | None = team_id
|
|
30
|
+
ok_hero_name: str = hero_name
|
|
31
|
+
|
|
32
|
+
# `Hero.id` is optional at the type level.
|
|
33
|
+
bad_hero_id: int = hero_id
|
|
34
|
+
if hero_id is not None:
|
|
35
|
+
ok_hero_id: int = hero_id
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
|
4
|
-
from sqlmodel.sql._expression_select_cls import Select as SQLModelSelect
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
class Team(SQLModel, table=True):
|
|
@@ -21,7 +20,7 @@ stmt = select(Hero, Team).join(Team, isouter=True).execution_options(populate_ex
|
|
|
21
20
|
|
|
22
21
|
# `execution_options()` should preserve the `Select[...]` generic, including outer-join `None`
|
|
23
22
|
# propagation for the joined entity.
|
|
24
|
-
ok_stmt
|
|
23
|
+
ok_stmt = stmt
|
|
25
24
|
|
|
26
25
|
with Session(engine) as session:
|
|
27
26
|
for hero, team in session.exec(stmt):
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Team(SQLModel, table=True):
|
|
7
|
+
id: int | None = Field(default=None, primary_key=True)
|
|
8
|
+
name: str = Field()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Hero(SQLModel, table=True):
|
|
12
|
+
id: int | None = Field(default=None, primary_key=True)
|
|
13
|
+
name: str = Field()
|
|
14
|
+
team_id: int | None = Field(default=None, foreign_key="team.id")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# SQLModel's generated `select()` overloads cap at 4 entities; ensure 5+ works.
|
|
18
|
+
stmt = select(Hero, Team, Hero.id, Team.id, Hero.name)
|
|
19
|
+
|
|
20
|
+
# Prefer returning SQLModel's own `Select[...]` wrapper, with best-effort element typing.
|
|
21
|
+
ok_stmt = stmt
|
|
22
|
+
|
|
23
|
+
engine = create_engine("sqlite://")
|
|
24
|
+
|
|
25
|
+
with Session(engine) as session:
|
|
26
|
+
for hero, team, hero_id, team_id, hero_name in session.exec(stmt):
|
|
27
|
+
ok_hero: Hero = hero
|
|
28
|
+
ok_team: Team = team
|
|
29
|
+
ok_team_id: int | None = team_id
|
|
30
|
+
ok_hero_name: str = hero_name
|
|
31
|
+
|
|
32
|
+
# `Hero.id` is optional at the type level.
|
|
33
|
+
bad_hero_id: int = hero_id
|
|
34
|
+
if hero_id is not None:
|
|
35
|
+
ok_hero_id: int = hero_id
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
|
4
|
+
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Hero(SQLModel, table=True):
|
|
8
|
+
id: int | None = Field(default=None, primary_key=True)
|
|
9
|
+
name: str = Field()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
engine = create_engine("sqlite://")
|
|
13
|
+
|
|
14
|
+
with Session(engine) as session:
|
|
15
|
+
result = session.execute(select(Hero))
|
|
16
|
+
|
|
17
|
+
# When `typed_execute=true`, `execute(select(Model))` should not degrade to `Result[Any]`.
|
|
18
|
+
row = result.one()
|
|
19
|
+
ok_row_hero: Hero = row[0]
|
|
20
|
+
bad_row_bool: bool = row[0]
|
|
21
|
+
# MYPY: error: Incompatible types in assignment (expression has type "Hero", variable has type "bool") [assignment]
|
|
22
|
+
|
|
23
|
+
ok_hero: Hero = result.scalars().one()
|
|
24
|
+
bad_bool: bool = result.scalars().one()
|
|
25
|
+
# MYPY: error: Incompatible types in assignment (expression has type "Hero", variable has type "bool") [assignment]
|
|
26
|
+
|
|
27
|
+
result2 = session.execute(select(Hero.id))
|
|
28
|
+
ok_id: int | None = result2.scalars().one()
|
|
29
|
+
bad_id: int = result2.scalars().one()
|
|
30
|
+
# MYPY: error: Incompatible types in assignment (expression has type "int | None", variable has type "int") [assignment]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
async def async_execute(session: AsyncSession) -> None:
|
|
34
|
+
result3 = await session.execute(select(Hero))
|
|
35
|
+
_ok_hero2: Hero = result3.scalars().one()
|
|
36
|
+
_bad_bool2: bool = result3.scalars().one()
|
|
37
|
+
# MYPY: error: Incompatible types in assignment (expression has type "Hero", variable has type "bool") [assignment]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async def async_execute_columns(session: AsyncSession) -> None:
|
|
41
|
+
result = await session.execute(select(Hero.id, Hero.name))
|
|
42
|
+
for hero_id, hero_name in result:
|
|
43
|
+
_ok_id: int | None = hero_id
|
|
44
|
+
_ok_name: str = hero_name
|
|
45
|
+
_bad_id: int = hero_id
|
|
46
|
+
# MYPY: error: Incompatible types in assignment (expression has type "int | None", variable has type "int") [assignment]
|
|
47
|
+
_bad_name: int = hero_name
|
|
48
|
+
# MYPY: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]
|