Skip to content

Commit fb63890

Browse files
authored
Merge branch 'main' into main
2 parents 3380fb0 + 0cbf2e6 commit fb63890

File tree

10 files changed

+101
-23
lines changed

10 files changed

+101
-23
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ repos:
1414
- id: end-of-file-fixer
1515
- id: trailing-whitespace
1616
- repo: https://github.com/astral-sh/ruff-pre-commit
17-
rev: v0.13.1
17+
rev: v0.13.3
1818
hooks:
1919
- id: ruff
2020
args:

docs/release-notes.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,23 @@
22

33
## Latest Changes
44

5+
## 0.0.26
6+
7+
### Fixes
8+
9+
* 🐛 Fix attribute handling in `model_dump` for compatibility with the latest Pydantic versions. PR [#1595](https://github.com/fastapi/sqlmodel/pull/1595) by [@spazm](https://github.com/spazm).
10+
511
### Docs
612

713
* 📝 Fix typo in `docs/tutorial/fastapi/simple-hero-api.md`. PR [#1583](https://github.com/fastapi/sqlmodel/pull/1583) by [@kofi-kusi](https://github.com/kofi-kusi).
814

915
### Internal
1016

17+
* ⬆ Bump mypy from 1.4.1 to 1.18.2. PR [#1560](https://github.com/fastapi/sqlmodel/pull/1560) by [@dependabot[bot]](https://github.com/apps/dependabot).
18+
* ✅ Add test that runs select with 3 or 4 arguments. PR [#1590](https://github.com/fastapi/sqlmodel/pull/1590) by [@svlandeg](https://github.com/svlandeg).
19+
* ⬆ Bump mkdocs-macros-plugin from 1.3.9 to 1.4.0. PR [#1581](https://github.com/fastapi/sqlmodel/pull/1581) by [@dependabot[bot]](https://github.com/apps/dependabot).
20+
* ⬆ Bump mkdocs-material from 9.6.20 to 9.6.21. PR [#1588](https://github.com/fastapi/sqlmodel/pull/1588) by [@dependabot[bot]](https://github.com/apps/dependabot).
21+
*[pre-commit.ci] pre-commit autoupdate. PR [#1584](https://github.com/fastapi/sqlmodel/pull/1584) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
1122
* ⬆ Bump tiangolo/issue-manager from 0.5.1 to 0.6.0. PR [#1589](https://github.com/fastapi/sqlmodel/pull/1589) by [@dependabot[bot]](https://github.com/apps/dependabot).
1223
* 👷 Update docs previews comment, single comment, add failure status. PR [#1586](https://github.com/fastapi/sqlmodel/pull/1586) by [@tiangolo](https://github.com/tiangolo).
1324
* ⬆ Bump markdown-include-variants from 0.0.4 to 0.0.5. PR [#1582](https://github.com/fastapi/sqlmodel/pull/1582) by [@dependabot[bot]](https://github.com/apps/dependabot).

pyproject.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,7 @@ show_contexts = true
9898

9999
[tool.mypy]
100100
strict = true
101-
102-
[[tool.mypy.overrides]]
103-
module = "sqlmodel.sql._expression_select_gen"
104-
warn_unused_ignores = false
101+
exclude = "sqlmodel.sql._expression_select_gen"
105102

106103
[[tool.mypy.overrides]]
107104
module = "docs_src.*"

requirements-docs.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
-e .
22
-r requirements-docs-tests.txt
3-
mkdocs-material==9.6.20
3+
mkdocs-material==9.6.21
44
mdx-include >=1.4.1,<2.0.0
55
mkdocs-redirects>=1.2.1,<1.3.0
66
pyyaml >=5.3.1,<7.0.0
@@ -14,5 +14,5 @@ cairosvg==2.8.2
1414
griffe-typingdoc==0.2.9
1515
# For griffe, it formats with black
1616
typer == 0.19.2
17-
mkdocs-macros-plugin==1.3.9
17+
mkdocs-macros-plugin==1.4.0
1818
markdown-include-variants==0.0.5

requirements-tests.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
-r requirements-docs-tests.txt
33
pytest >=7.0.1,<9.0.0
44
coverage[toml] >=6.2,<8.0
5-
mypy ==1.4.1
5+
# Remove when support for Python 3.8 is dropped
6+
mypy ==1.14.1; python_version < "3.9"
7+
mypy ==1.18.2; python_version >= "3.9"
68
ruff ==0.13.2
79
# For FastAPI tests
810
fastapi >=0.103.2

scripts/lint.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ set -e
44
set -x
55

66
mypy sqlmodel
7+
mypy tests/test_select_typing.py
78
ruff check sqlmodel tests docs_src scripts
89
ruff format sqlmodel tests docs_src scripts --check

sqlmodel/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "0.0.25"
1+
__version__ = "0.0.26"
22

33
# Re-export from SQLAlchemy
44
from sqlalchemy.engine import create_engine as create_engine

sqlmodel/_compat.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ def init_pydantic_private_attrs(new_object: InstanceOrType["SQLModel"]) -> None:
123123
object.__setattr__(new_object, "__pydantic_private__", None)
124124

125125
def get_annotations(class_dict: Dict[str, Any]) -> Dict[str, Any]:
126-
return class_dict.get("__annotations__", {})
126+
return class_dict.get("__annotations__", {}) # type: ignore[no-any-return]
127127

128128
def is_table_model_class(cls: Type[Any]) -> bool:
129129
config = getattr(cls, "model_config", {})
@@ -180,7 +180,7 @@ def is_field_noneable(field: "FieldInfo") -> bool:
180180
if not field.is_required():
181181
if field.default is Undefined:
182182
return False
183-
if field.annotation is None or field.annotation is NoneType: # type: ignore[comparison-overlap]
183+
if field.annotation is None or field.annotation is NoneType:
184184
return True
185185
return False
186186
return False
@@ -509,7 +509,7 @@ def _calculate_keys(
509509
keys -= update.keys()
510510

511511
if exclude:
512-
keys -= {k for k, v in exclude.items() if ValueItems.is_true(v)}
512+
keys -= {str(k) for k, v in exclude.items() if ValueItems.is_true(v)}
513513

514514
return keys
515515

sqlmodel/main.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import inspect as inspect_module
24
import ipaddress
35
import uuid
@@ -115,7 +117,8 @@ def __dataclass_transform__(
115117
return lambda a: a
116118

117119

118-
class FieldInfo(PydanticFieldInfo):
120+
class FieldInfo(PydanticFieldInfo): # type: ignore[misc]
121+
# mypy - ignore that PydanticFieldInfo is @final
119122
def __init__(self, default: Any = Undefined, **kwargs: Any) -> None:
120123
primary_key = kwargs.pop("primary_key", False)
121124
nullable = kwargs.pop("nullable", Undefined)
@@ -671,7 +674,7 @@ def __init__(
671674
setattr(cls, rel_name, rel_info.sa_relationship) # Fix #315
672675
continue
673676
raw_ann = cls.__annotations__[rel_name]
674-
origin = get_origin(raw_ann)
677+
origin: Any = get_origin(raw_ann)
675678
if origin is Mapped:
676679
ann = raw_ann.__args__[0]
677680
else:
@@ -934,27 +937,27 @@ def model_dump(
934937
mode: Union[Literal["json", "python"], str] = "python",
935938
include: Union[IncEx, None] = None,
936939
exclude: Union[IncEx, None] = None,
937-
context: Union[Any, None] = None,
940+
context: Union[Any, None] = None, # v2.7
938941
by_alias: Union[bool, None] = None,
939942
exclude_unset: bool = False,
940943
exclude_defaults: bool = False,
941944
exclude_none: bool = False,
945+
exclude_computed_fields: bool = False, # v2.12
942946
round_trip: bool = False,
943947
warnings: Union[bool, Literal["none", "warn", "error"]] = True,
944-
fallback: Union[Callable[[Any], Any], None] = None,
945-
serialize_as_any: bool = False,
948+
fallback: Union[Callable[[Any], Any], None] = None, # v2.11
949+
serialize_as_any: bool = False, # v2.7
946950
) -> Dict[str, Any]:
947951
if PYDANTIC_MINOR_VERSION < (2, 11):
948952
by_alias = by_alias or False
953+
extra_kwargs: Dict[str, Any] = {}
949954
if PYDANTIC_MINOR_VERSION >= (2, 7):
950-
extra_kwargs: Dict[str, Any] = {
951-
"context": context,
952-
"serialize_as_any": serialize_as_any,
953-
}
955+
extra_kwargs["context"] = context
956+
extra_kwargs["serialize_as_any"] = serialize_as_any
954957
if PYDANTIC_MINOR_VERSION >= (2, 11):
955958
extra_kwargs["fallback"] = fallback
956-
else:
957-
extra_kwargs = {}
959+
if PYDANTIC_MINOR_VERSION >= (2, 12):
960+
extra_kwargs["exclude_computed_fields"] = exclude_computed_fields
958961
if IS_PYDANTIC_V2:
959962
return super().model_dump(
960963
mode=mode,

tests/test_select_typing.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from typing import Optional
2+
3+
from sqlmodel import Field, Session, SQLModel, create_engine, select
4+
from sqlmodel.pool import StaticPool
5+
6+
7+
def test_fields() -> None:
8+
class Hero(SQLModel, table=True):
9+
id: Optional[int] = Field(default=None, primary_key=True)
10+
name: str
11+
secret_name: str
12+
age: Optional[int] = None
13+
food: Optional[str] = None
14+
15+
engine = create_engine(
16+
"sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool
17+
)
18+
19+
SQLModel.metadata.create_all(engine)
20+
21+
with Session(engine) as session:
22+
session.add(Hero(name="Deadpond", secret_name="Dive Wilson"))
23+
session.add(
24+
Hero(name="Spider-Boy", secret_name="Pedro Parqueador", food="pizza")
25+
)
26+
session.add(Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48))
27+
28+
session.commit()
29+
30+
# check typing of select with 3 fields
31+
with Session(engine) as session:
32+
statement_3 = select(Hero.id, Hero.name, Hero.secret_name)
33+
results_3 = session.exec(statement_3)
34+
for hero_3 in results_3:
35+
assert len(hero_3) == 3
36+
name_3: str = hero_3[1]
37+
assert type(name_3) is str
38+
assert type(hero_3[0]) is int
39+
assert type(hero_3[2]) is str
40+
41+
# check typing of select with 4 fields
42+
with Session(engine) as session:
43+
statement_4 = select(Hero.id, Hero.name, Hero.secret_name, Hero.age)
44+
results_4 = session.exec(statement_4)
45+
for hero_4 in results_4:
46+
assert len(hero_4) == 4
47+
name_4: str = hero_4[1]
48+
assert type(name_4) is str
49+
assert type(hero_4[0]) is int
50+
assert type(hero_4[2]) is str
51+
assert type(hero_4[3]) in [int, type(None)]
52+
53+
# check typing of select with 5 fields: currently runs but doesn't pass mypy
54+
# with Session(engine) as session:
55+
# statement_5 = select(Hero.id, Hero.name, Hero.secret_name, Hero.age, Hero.food)
56+
# results_5 = session.exec(statement_5)
57+
# for hero_5 in results_5:
58+
# assert len(hero_5) == 5
59+
# name_5: str = hero_5[1]
60+
# assert type(name_5) is str
61+
# assert type(hero_5[0]) is int
62+
# assert type(hero_5[2]) is str
63+
# assert type(hero_5[3]) in [int, type(None)]
64+
# assert type(hero_5[4]) in [str, type(None)]

0 commit comments

Comments
 (0)