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.
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/.gitignore +2 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/AGENTS.md +5 -2
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/LICENSE +1 -1
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/PKG-INFO +29 -6
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/README.md +25 -2
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/pyproject.toml +3 -3
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/src/sqlmodel_mypy/__init__.py +1 -1
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/src/sqlmodel_mypy/plugin.py +654 -60
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/src/sqlmodel_mypy/transform.py +72 -0
- sqlmodel_mypy_plugin-0.19.0/tests/mypy/configs/mypy-plugin-strict-typed-execute.ini +20 -0
- sqlmodel_mypy_plugin-0.19.0/tests/mypy/modules/aliased_joins.py +32 -0
- sqlmodel_mypy_plugin-0.19.0/tests/mypy/modules/field_validation_alias.py +10 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/getattr_column_property.py +16 -0
- sqlmodel_mypy_plugin-0.19.0/tests/mypy/modules/select_varargs.py +36 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/session_exec.py +9 -2
- sqlmodel_mypy_plugin-0.19.0/tests/mypy/modules/session_execute_typed.py +32 -0
- sqlmodel_mypy_plugin-0.19.0/tests/mypy/modules/table_dunders.py +24 -0
- sqlmodel_mypy_plugin-0.19.0/tests/mypy/modules/table_via_model_config.py +35 -0
- sqlmodel_mypy_plugin-0.19.0/tests/mypy/modules/tuple_expression.py +14 -0
- sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin-strict-typed-execute_ini/session_execute_typed.py +35 -0
- sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin-strict_ini/aliased_joins.py +32 -0
- {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
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/execution_options.py +0 -1
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/joins.py +0 -3
- sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin-strict_ini/select_varargs.py +37 -0
- sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin-strict_ini/table_via_model_config.py +37 -0
- sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin-strict_ini/tuple_expression.py +14 -0
- sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin_ini/aliased_joins.py +32 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/execution_options.py +0 -1
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/joins.py +0 -3
- sqlmodel_mypy_plugin-0.19.0/tests/mypy/outputs/mypy-plugin_ini/select_varargs.py +37 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/test_mypy.py +7 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/test_plugin_unit.py +940 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/test_transform_unit.py +42 -0
- sqlmodel_mypy_plugin-0.16.0/ROADMAP.md +0 -311
- sqlmodel_mypy_plugin-0.16.0/tests/mypy/modules/select_varargs.py +0 -18
- sqlmodel_mypy_plugin-0.16.0/tests/mypy/modules/table_dunders.py +0 -12
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/.pre-commit-config.yaml +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/src/sqlmodel_mypy/persisted.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/src/sqlmodel_mypy/py.typed +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/conftest.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/configs/mypy-plugin-no-untyped.ini +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/configs/mypy-plugin-pydantic-first.ini +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/configs/mypy-plugin-pydantic-last.ini +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/configs/mypy-plugin-strict-no-explicit-any.ini +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/configs/mypy-plugin-strict.ini +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/configs/mypy-plugin.ini +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/configs/pyproject-plugin.toml +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/aliases.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/annotated_field_metadata.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/constructor.py +0 -0
- {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
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/docs_parity_read_connected_data.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/docs_parity_relationship_attributes.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/docs_parity_update_patterns.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/execution_options.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/expression_typing_strict.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/expressions.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/field_sa_type.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/generics.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/inheritance.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/joins.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/label.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/many_to_many_relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/mapped_fields.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/model_config.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/model_construct.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/nullable_defaults.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/persisted_helpers.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/relationship_comparators.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/relationship_expressions.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/selectinload.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/session_get.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/table_model_awareness.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/modules/untyped_fields.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-pydantic-first_ini/relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-pydantic-last_ini/relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/aliases.py +0 -0
- {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
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/constructor.py +0 -0
- {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
- {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
- {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
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/generics.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/label.py +0 -0
- {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
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/mapped_fields.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/model_construct.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/nullable_defaults.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/persisted_helpers.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/relationship_comparators.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin-strict_ini/relationships.py +0 -0
- {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
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/aliases.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/constructor.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/expressions.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/inheritance.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/label.py +0 -0
- {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
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/model_construct.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/nullable_defaults.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/relationship_comparators.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/relationship_expressions.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/relationships.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/table_model_awareness.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/mypy-plugin_ini/untyped_fields.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/mypy/outputs/pyproject-plugin_toml/constructor.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/smoke_test.py +0 -0
- {sqlmodel_mypy_plugin-0.16.0 → sqlmodel_mypy_plugin-0.19.0}/tests/test_persisted_helpers.py +0 -0
|
@@ -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
|
-
-
|
|
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**
|
|
89
|
+
- GitHub Actions publishes to PyPI via **trusted publishing**
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlmodel-mypy-plugin
|
|
3
|
-
Version: 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:
|
|
9
|
+
Author: sertione
|
|
10
10
|
License: MIT License
|
|
11
11
|
|
|
12
|
-
Copyright (c) 2026
|
|
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 ::
|
|
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**
|
|
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**
|
|
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.
|
|
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 = "
|
|
12
|
+
authors = [{ name = "sertione" }]
|
|
13
13
|
keywords = ["mypy", "plugin", "sqlmodel", "pydantic", "sqlalchemy", "typing"]
|
|
14
14
|
classifiers = [
|
|
15
|
-
"Development Status ::
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
16
|
"Intended Audience :: Developers",
|
|
17
17
|
"License :: OSI Approved :: MIT License",
|
|
18
18
|
"Programming Language :: Python :: 3 :: Only",
|