diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 585ffc0455..855f1e2d9f 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
- python-version: ["3.6.15", "3.7", "3.8", "3.9", "3.10"]
+ python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
fail-fast: false
steps:
@@ -54,7 +54,6 @@ jobs:
if: steps.cache.outputs.cache-hit != 'true'
run: python -m poetry install
- name: Lint
- if: ${{ matrix.python-version != '3.6.15' }}
run: python -m poetry run bash scripts/lint.sh
- run: mkdir coverage
- name: Test
diff --git a/README.md b/README.md
index 5721f1cdb0..df1e3906b9 100644
--- a/README.md
+++ b/README.md
@@ -50,7 +50,7 @@ It combines SQLAlchemy and Pydantic and tries to simplify the code you write as
## Requirements
-A recent and currently supported version of Python (right now, Python supports versions 3.6 and above).
+A recent and currently supported version of Python (right now, Python supports versions 3.7 and above).
As **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.
diff --git a/docs/contributing.md b/docs/contributing.md
index f2964fba9b..3682c23ae1 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -8,7 +8,7 @@ If you already cloned the repository and you know that you need to deep dive in
### Python
-SQLModel supports Python 3.6 and above, but for development you should have at least **Python 3.7**.
+SQLModel supports Python 3.7 and above.
### Poetry
diff --git a/docs/features.md b/docs/features.md
index 09de0c17f9..2d5e11d84f 100644
--- a/docs/features.md
+++ b/docs/features.md
@@ -12,7 +12,7 @@ Nevertheless, SQLModel is completely **independent** of FastAPI and can be used
## Just Modern Python
-It's all based on standard modern **Python** type annotations. No new syntax to learn. Just standard modern Python.
+It's all based on standard modern **Python** type annotations. No new syntax to learn. Just standard modern Python.
If you need a 2 minute refresher of how to use Python types (even if you don't use SQLModel or FastAPI), check the FastAPI tutorial section: Python types intro.
diff --git a/docs/index.md b/docs/index.md
index 5721f1cdb0..df1e3906b9 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -50,7 +50,7 @@ It combines SQLAlchemy and Pydantic and tries to simplify the code you write as
## Requirements
-A recent and currently supported version of Python (right now, Python supports versions 3.6 and above).
+A recent and currently supported version of Python (right now, Python supports versions 3.7 and above).
As **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.
diff --git a/docs/tutorial/index.md b/docs/tutorial/index.md
index 33cf6226c4..03bbc80e49 100644
--- a/docs/tutorial/index.md
+++ b/docs/tutorial/index.md
@@ -64,7 +64,7 @@ $ cd sqlmodel-tutorial
Make sure you have an officially supported version of Python.
-Currently it is **Python 3.6** and above (Python 3.5 was already deprecated).
+Currently it is **Python 3.7** and above (Python 3.6 was already deprecated).
You can check which version you have with:
@@ -81,11 +81,11 @@ There's a chance that you have multiple Python versions installed.
You might want to try with the specific versions, for example with:
+* `python3.11`
* `python3.10`
* `python3.9`
* `python3.8`
* `python3.7`
-* `python3.6`
The code would look like this:
diff --git a/docs_src/tutorial/many_to_many/tutorial003.py b/docs_src/tutorial/many_to_many/tutorial003.py
index 1e03c4af89..cec6e56560 100644
--- a/docs_src/tutorial/many_to_many/tutorial003.py
+++ b/docs_src/tutorial/many_to_many/tutorial003.py
@@ -3,25 +3,12 @@
from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select
-class HeroTeamLink(SQLModel, table=True):
- team_id: Optional[int] = Field(
- default=None, foreign_key="team.id", primary_key=True
- )
- hero_id: Optional[int] = Field(
- default=None, foreign_key="hero.id", primary_key=True
- )
- is_training: bool = False
-
- team: "Team" = Relationship(back_populates="hero_links")
- hero: "Hero" = Relationship(back_populates="team_links")
-
-
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
- hero_links: List[HeroTeamLink] = Relationship(back_populates="team")
+ hero_links: List["HeroTeamLink"] = Relationship(back_populates="team")
class Hero(SQLModel, table=True):
@@ -30,7 +17,20 @@ class Hero(SQLModel, table=True):
secret_name: str
age: Optional[int] = Field(default=None, index=True)
- team_links: List[HeroTeamLink] = Relationship(back_populates="hero")
+ team_links: List["HeroTeamLink"] = Relationship(back_populates="hero")
+
+
+class HeroTeamLink(SQLModel, table=True):
+ team_id: Optional[int] = Field(
+ default=None, foreign_key="team.id", primary_key=True
+ )
+ hero_id: Optional[int] = Field(
+ default=None, foreign_key="hero.id", primary_key=True
+ )
+ is_training: bool = False
+
+ team: "Team" = Relationship(back_populates="hero_links")
+ hero: "Hero" = Relationship(back_populates="team_links")
sqlite_file_name = "database.db"
diff --git a/docs_src/tutorial/relationship_attributes/back_populates/tutorial003.py b/docs_src/tutorial/relationship_attributes/back_populates/tutorial003.py
index 98e197002e..8d91a0bc25 100644
--- a/docs_src/tutorial/relationship_attributes/back_populates/tutorial003.py
+++ b/docs_src/tutorial/relationship_attributes/back_populates/tutorial003.py
@@ -3,6 +3,21 @@
from sqlmodel import Field, Relationship, SQLModel, create_engine
+class Hero(SQLModel, table=True):
+ id: Optional[int] = Field(default=None, primary_key=True)
+ name: str = Field(index=True)
+ secret_name: str
+ age: Optional[int] = Field(default=None, index=True)
+
+ team_id: Optional[int] = Field(default=None, foreign_key="team.id")
+ team: Optional["Team"] = Relationship(back_populates="heroes")
+
+ weapon_id: Optional[int] = Field(default=None, foreign_key="weapon.id")
+ weapon: Optional["Weapon"] = Relationship(back_populates="hero")
+
+ powers: List["Power"] = Relationship(back_populates="hero")
+
+
class Weapon(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
@@ -26,21 +41,6 @@ class Team(SQLModel, table=True):
heroes: List["Hero"] = Relationship(back_populates="team")
-class Hero(SQLModel, table=True):
- id: Optional[int] = Field(default=None, primary_key=True)
- name: str = Field(index=True)
- secret_name: str
- age: Optional[int] = Field(default=None, index=True)
-
- team_id: Optional[int] = Field(default=None, foreign_key="team.id")
- team: Optional[Team] = Relationship(back_populates="heroes")
-
- weapon_id: Optional[int] = Field(default=None, foreign_key="weapon.id")
- weapon: Optional[Weapon] = Relationship(back_populates="hero")
-
- powers: List[Power] = Relationship(back_populates="hero")
-
-
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
diff --git a/pyproject.toml b/pyproject.toml
index e3b1d3c279..a0cec7b9da 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -17,10 +17,11 @@ classifiers = [
"Intended Audience :: System Administrators",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3 :: Only",
- "Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
"Topic :: Database",
"Topic :: Database :: Database Engines/Servers",
"Topic :: Internet",
@@ -30,10 +31,9 @@ classifiers = [
]
[tool.poetry.dependencies]
-python = "^3.6.1"
-SQLAlchemy = ">=1.4.17,<=1.4.41"
+python = "^3.7"
+SQLAlchemy = ">=2.0.0,<=2.0.11"
pydantic = "^1.8.2"
-sqlalchemy2-stubs = {version = "*", allow-prereleases = true}
[tool.poetry.dev-dependencies]
pytest = "^7.0.1"
@@ -50,8 +50,8 @@ fastapi = "^0.68.1"
requests = "^2.26.0"
autoflake = "^1.4"
isort = "^5.9.3"
-async_generator = {version = "*", python = "~3.6"}
-async-exit-stack = {version = "*", python = "~3.6"}
+async_generator = {version = "*", python = "~3.7"}
+async-exit-stack = {version = "*", python = "~3.7"}
[build-system]
requires = ["poetry-core"]
diff --git a/scripts/lint.sh b/scripts/lint.sh
index 02568cda6b..4191d90f1f 100755
--- a/scripts/lint.sh
+++ b/scripts/lint.sh
@@ -7,5 +7,3 @@ mypy sqlmodel
flake8 sqlmodel tests docs_src
black sqlmodel tests docs_src --check
isort sqlmodel tests docs_src scripts --check-only
-# TODO: move this to test.sh after deprecating Python 3.6
-CHECK_JINJA=1 python scripts/generate_select.py
diff --git a/scripts/test.sh b/scripts/test.sh
index 9b758bdbdf..1460a9c7ec 100755
--- a/scripts/test.sh
+++ b/scripts/test.sh
@@ -3,6 +3,7 @@
set -e
set -x
+CHECK_JINJA=1 python scripts/generate_select.py
coverage run -m pytest tests
coverage combine
coverage report --show-missing
diff --git a/sqlmodel/__init__.py b/sqlmodel/__init__.py
index 720aa8c929..7028ce0018 100644
--- a/sqlmodel/__init__.py
+++ b/sqlmodel/__init__.py
@@ -21,7 +21,6 @@
from sqlalchemy.schema import PrimaryKeyConstraint as PrimaryKeyConstraint
from sqlalchemy.schema import Sequence as Sequence
from sqlalchemy.schema import Table as Table
-from sqlalchemy.schema import ThreadLocalMetaData as ThreadLocalMetaData
from sqlalchemy.schema import UniqueConstraint as UniqueConstraint
from sqlalchemy.sql import alias as alias
from sqlalchemy.sql import all_ as all_
@@ -71,7 +70,7 @@
from sqlalchemy.sql import outerjoin as outerjoin
from sqlalchemy.sql import outparam as outparam
from sqlalchemy.sql import over as over
-from sqlalchemy.sql import subquery as subquery
+from sqlalchemy.sql import Subquery as Subquery
from sqlalchemy.sql import table as table
from sqlalchemy.sql import tablesample as tablesample
from sqlalchemy.sql import text as text
diff --git a/sqlmodel/engine/create.py b/sqlmodel/engine/create.py
index b2d567b1b1..97481259e2 100644
--- a/sqlmodel/engine/create.py
+++ b/sqlmodel/engine/create.py
@@ -136,4 +136,4 @@ def create_engine(
if not isinstance(query_cache_size, _DefaultPlaceholder):
current_kwargs["query_cache_size"] = query_cache_size
current_kwargs.update(kwargs)
- return _create_engine(url, **current_kwargs) # type: ignore
+ return _create_engine(url, **current_kwargs)
diff --git a/sqlmodel/engine/result.py b/sqlmodel/engine/result.py
index 7a25422227..650dd92b27 100644
--- a/sqlmodel/engine/result.py
+++ b/sqlmodel/engine/result.py
@@ -1,4 +1,4 @@
-from typing import Generic, Iterator, List, Optional, TypeVar
+from typing import Generic, Iterator, Optional, Sequence, Tuple, TypeVar
from sqlalchemy.engine.result import Result as _Result
from sqlalchemy.engine.result import ScalarResult as _ScalarResult
@@ -6,24 +6,24 @@
_T = TypeVar("_T")
-class ScalarResult(_ScalarResult, Generic[_T]):
- def all(self) -> List[_T]:
+class ScalarResult(_ScalarResult[_T], Generic[_T]):
+ def all(self) -> Sequence[_T]:
return super().all()
- def partitions(self, size: Optional[int] = None) -> Iterator[List[_T]]:
+ def partitions(self, size: Optional[int] = None) -> Iterator[Sequence[_T]]:
return super().partitions(size)
- def fetchall(self) -> List[_T]:
+ def fetchall(self) -> Sequence[_T]:
return super().fetchall()
- def fetchmany(self, size: Optional[int] = None) -> List[_T]:
+ def fetchmany(self, size: Optional[int] = None) -> Sequence[_T]:
return super().fetchmany(size)
def __iter__(self) -> Iterator[_T]:
return super().__iter__()
def __next__(self) -> _T:
- return super().__next__() # type: ignore
+ return super().__next__()
def first(self) -> Optional[_T]:
return super().first()
@@ -32,48 +32,8 @@ def one_or_none(self) -> Optional[_T]:
return super().one_or_none()
def one(self) -> _T:
- return super().one() # type: ignore
+ return super().one()
-class Result(_Result, Generic[_T]):
- def scalars(self, index: int = 0) -> ScalarResult[_T]:
- return super().scalars(index) # type: ignore
-
- def __iter__(self) -> Iterator[_T]: # type: ignore
- return super().__iter__() # type: ignore
-
- def __next__(self) -> _T: # type: ignore
- return super().__next__() # type: ignore
-
- def partitions(self, size: Optional[int] = None) -> Iterator[List[_T]]: # type: ignore
- return super().partitions(size) # type: ignore
-
- def fetchall(self) -> List[_T]: # type: ignore
- return super().fetchall() # type: ignore
-
- def fetchone(self) -> Optional[_T]: # type: ignore
- return super().fetchone() # type: ignore
-
- def fetchmany(self, size: Optional[int] = None) -> List[_T]: # type: ignore
- return super().fetchmany() # type: ignore
-
- def all(self) -> List[_T]: # type: ignore
- return super().all() # type: ignore
-
- def first(self) -> Optional[_T]: # type: ignore
- return super().first() # type: ignore
-
- def one_or_none(self) -> Optional[_T]: # type: ignore
- return super().one_or_none() # type: ignore
-
- def scalar_one(self) -> _T:
- return super().scalar_one() # type: ignore
-
- def scalar_one_or_none(self) -> Optional[_T]:
- return super().scalar_one_or_none()
-
- def one(self) -> _T: # type: ignore
- return super().one() # type: ignore
-
- def scalar(self) -> Optional[_T]:
- return super().scalar()
+class Result(_Result[Tuple[_T]], Generic[_T]):
+ ...
diff --git a/sqlmodel/main.py b/sqlmodel/main.py
index d95c498507..d05fdcc8b7 100644
--- a/sqlmodel/main.py
+++ b/sqlmodel/main.py
@@ -11,6 +11,7 @@
Callable,
ClassVar,
Dict,
+ ForwardRef,
List,
Mapping,
Optional,
@@ -29,7 +30,7 @@
from pydantic.fields import FieldInfo as PydanticFieldInfo
from pydantic.fields import ModelField, Undefined, UndefinedType
from pydantic.main import ModelMetaclass, validate_model
-from pydantic.typing import ForwardRef, NoArgAnyCallable, resolve_annotations
+from pydantic.typing import NoArgAnyCallable, resolve_annotations
from pydantic.utils import ROOT_KEY, Representation
from sqlalchemy import Boolean, Column, Date, DateTime
from sqlalchemy import Enum as sa_Enum
@@ -478,6 +479,7 @@ class SQLModel(BaseModel, metaclass=SQLModelMetaclass, registry=default_registry
__sqlmodel_relationships__: ClassVar[Dict[str, RelationshipProperty]] # type: ignore
__name__: ClassVar[str]
metadata: ClassVar[MetaData]
+ __allow_unmapped__ = True # https://docs.sqlalchemy.org/en/20/changelog/migration_20.html#migration-20-step-six
class Config:
orm_mode = True
@@ -521,7 +523,7 @@ def __setattr__(self, name: str, value: Any) -> None:
return
else:
# Set in SQLAlchemy, before Pydantic to trigger events and updates
- if getattr(self.__config__, "table", False) and is_instrumented(self, name):
+ if getattr(self.__config__, "table", False) and is_instrumented(self, name): # type: ignore
set_attribute(self, name, value)
# Set in Pydantic model to trigger possible validation changes, only for
# non relationship values
diff --git a/sqlmodel/orm/session.py b/sqlmodel/orm/session.py
index 1692fdcbcb..9a07956d92 100644
--- a/sqlmodel/orm/session.py
+++ b/sqlmodel/orm/session.py
@@ -1,11 +1,22 @@
-from typing import Any, Mapping, Optional, Sequence, Type, TypeVar, Union, overload
+from typing import (
+ Any,
+ Dict,
+ Mapping,
+ Optional,
+ Sequence,
+ Type,
+ TypeVar,
+ Union,
+ overload,
+)
from sqlalchemy import util
+from sqlalchemy.orm import Mapper as _Mapper
from sqlalchemy.orm import Query as _Query
from sqlalchemy.orm import Session as _Session
from sqlalchemy.sql.base import Executable as _Executable
+from sqlalchemy.sql.selectable import ForUpdateArg as _ForUpdateArg
from sqlmodel.sql.expression import Select, SelectOfScalar
-from typing_extensions import Literal
from ..engine.result import Result, ScalarResult
from ..sql.base import Executable
@@ -21,7 +32,7 @@ def exec(
*,
params: Optional[Union[Mapping[str, Any], Sequence[Mapping[str, Any]]]] = None,
execution_options: Mapping[str, Any] = util.EMPTY_DICT,
- bind_arguments: Optional[Mapping[str, Any]] = None,
+ bind_arguments: Optional[Dict[str, Any]] = None,
_parent_execute_state: Optional[Any] = None,
_add_event: Optional[Any] = None,
**kw: Any,
@@ -35,7 +46,7 @@ def exec(
*,
params: Optional[Union[Mapping[str, Any], Sequence[Mapping[str, Any]]]] = None,
execution_options: Mapping[str, Any] = util.EMPTY_DICT,
- bind_arguments: Optional[Mapping[str, Any]] = None,
+ bind_arguments: Optional[Dict[str, Any]] = None,
_parent_execute_state: Optional[Any] = None,
_add_event: Optional[Any] = None,
**kw: Any,
@@ -52,7 +63,7 @@ def exec(
*,
params: Optional[Union[Mapping[str, Any], Sequence[Mapping[str, Any]]]] = None,
execution_options: Mapping[str, Any] = util.EMPTY_DICT,
- bind_arguments: Optional[Mapping[str, Any]] = None,
+ bind_arguments: Optional[Dict[str, Any]] = None,
_parent_execute_state: Optional[Any] = None,
_add_event: Optional[Any] = None,
**kw: Any,
@@ -74,8 +85,8 @@ def execute(
self,
statement: _Executable,
params: Optional[Union[Mapping[str, Any], Sequence[Mapping[str, Any]]]] = None,
- execution_options: Optional[Mapping[str, Any]] = util.EMPTY_DICT,
- bind_arguments: Optional[Mapping[str, Any]] = None,
+ execution_options: Mapping[str, Any] = util.EMPTY_DICT,
+ bind_arguments: Optional[Dict[str, Any]] = None,
_parent_execute_state: Optional[Any] = None,
_add_event: Optional[Any] = None,
**kw: Any,
@@ -118,17 +129,18 @@ def query(self, *entities: Any, **kwargs: Any) -> "_Query[Any]":
Or otherwise you might want to use `session.execute()` instead of
`session.query()`.
"""
- return super().query(*entities, **kwargs)
+ return super().query(*entities, **kwargs) # type: ignore
def get(
self,
- entity: Type[_TSelectParam],
+ entity: Union[Type[_TSelectParam], "_Mapper[_TSelectParam]"],
ident: Any,
options: Optional[Sequence[Any]] = None,
populate_existing: bool = False,
- with_for_update: Optional[Union[Literal[True], Mapping[str, Any]]] = None,
+ with_for_update: Optional[_ForUpdateArg] = None,
identity_token: Optional[Any] = None,
- execution_options: Optional[Mapping[Any, Any]] = util.EMPTY_DICT,
+ execution_options: Mapping[Any, Any] = util.EMPTY_DICT,
+ bind_arguments: Optional[Dict[str, Any]] = None,
) -> Optional[_TSelectParam]:
return super().get(
entity,
@@ -138,4 +150,5 @@ def get(
with_for_update=with_for_update,
identity_token=identity_token,
execution_options=execution_options,
+ bind_arguments=bind_arguments,
)
diff --git a/sqlmodel/sql/expression.py b/sqlmodel/sql/expression.py
index 31c0bc1a1e..a0ac1bd9d9 100644
--- a/sqlmodel/sql/expression.py
+++ b/sqlmodel/sql/expression.py
@@ -1,6 +1,5 @@
# WARNING: do not modify this code, it is generated by expression.py.jinja2
-import sys
from datetime import datetime
from typing import (
TYPE_CHECKING,
@@ -12,7 +11,6 @@
Type,
TypeVar,
Union,
- cast,
overload,
)
from uuid import UUID
@@ -24,36 +22,17 @@
_TSelect = TypeVar("_TSelect")
-# Workaround Generics incompatibility in Python 3.6
-# Ref: https://github.com/python/typing/issues/449#issuecomment-316061322
-if sys.version_info.minor >= 7:
- class Select(_Select, Generic[_TSelect]):
- inherit_cache = True
+class Select(_Select[Tuple[_TSelect]], Generic[_TSelect]):
+ inherit_cache = True
- # This is not comparable to sqlalchemy.sql.selectable.ScalarSelect, that has a different
- # purpose. This is the same as a normal SQLAlchemy Select class where there's only one
- # entity, so the result will be converted to a scalar by default. This way writing
- # for loops on the results will feel natural.
- class SelectOfScalar(_Select, Generic[_TSelect]):
- inherit_cache = True
-else:
- from typing import GenericMeta # type: ignore
-
- class GenericSelectMeta(GenericMeta, _Select.__class__): # type: ignore
- pass
-
- class _Py36Select(_Select, Generic[_TSelect], metaclass=GenericSelectMeta):
- inherit_cache = True
-
- class _Py36SelectOfScalar(_Select, Generic[_TSelect], metaclass=GenericSelectMeta):
- inherit_cache = True
-
- # Cast them for editors to work correctly, from several tricks tried, this works
- # for both VS Code and PyCharm
- Select = cast("Select", _Py36Select) # type: ignore
- SelectOfScalar = cast("SelectOfScalar", _Py36SelectOfScalar) # type: ignore
+# This is not comparable to sqlalchemy.sql.selectable.ScalarSelect, that has a different
+# purpose. This is the same as a normal SQLAlchemy Select class where there's only one
+# entity, so the result will be converted to a scalar by default. This way writing
+# for loops on the results will feel natural.
+class SelectOfScalar(_Select[Tuple[_TSelect]], Generic[_TSelect]):
+ inherit_cache = True
if TYPE_CHECKING: # pragma: no cover
@@ -138,12 +117,12 @@ class _Py36SelectOfScalar(_Select, Generic[_TSelect], metaclass=GenericSelectMet
@overload
-def select(entity_0: _TScalar_0, **kw: Any) -> SelectOfScalar[_TScalar_0]: # type: ignore
+def select(entity_0: _TScalar_0) -> SelectOfScalar[_TScalar_0]: # type: ignore
...
@overload
-def select(entity_0: Type[_TModel_0], **kw: Any) -> SelectOfScalar[_TModel_0]: # type: ignore
+def select(entity_0: Type[_TModel_0]) -> SelectOfScalar[_TModel_0]: # type: ignore
...
@@ -154,7 +133,6 @@ def select(entity_0: Type[_TModel_0], **kw: Any) -> SelectOfScalar[_TModel_0]:
def select( # type: ignore
entity_0: _TScalar_0,
entity_1: _TScalar_1,
- **kw: Any,
) -> Select[Tuple[_TScalar_0, _TScalar_1]]:
...
@@ -163,7 +141,6 @@ def select( # type: ignore
def select( # type: ignore
entity_0: _TScalar_0,
entity_1: Type[_TModel_1],
- **kw: Any,
) -> Select[Tuple[_TScalar_0, _TModel_1]]:
...
@@ -172,7 +149,6 @@ def select( # type: ignore
def select( # type: ignore
entity_0: Type[_TModel_0],
entity_1: _TScalar_1,
- **kw: Any,
) -> Select[Tuple[_TModel_0, _TScalar_1]]:
...
@@ -181,7 +157,6 @@ def select( # type: ignore
def select( # type: ignore
entity_0: Type[_TModel_0],
entity_1: Type[_TModel_1],
- **kw: Any,
) -> Select[Tuple[_TModel_0, _TModel_1]]:
...
@@ -191,7 +166,6 @@ def select( # type: ignore
entity_0: _TScalar_0,
entity_1: _TScalar_1,
entity_2: _TScalar_2,
- **kw: Any,
) -> Select[Tuple[_TScalar_0, _TScalar_1, _TScalar_2]]:
...
@@ -201,7 +175,6 @@ def select( # type: ignore
entity_0: _TScalar_0,
entity_1: _TScalar_1,
entity_2: Type[_TModel_2],
- **kw: Any,
) -> Select[Tuple[_TScalar_0, _TScalar_1, _TModel_2]]:
...
@@ -211,7 +184,6 @@ def select( # type: ignore
entity_0: _TScalar_0,
entity_1: Type[_TModel_1],
entity_2: _TScalar_2,
- **kw: Any,
) -> Select[Tuple[_TScalar_0, _TModel_1, _TScalar_2]]:
...
@@ -221,7 +193,6 @@ def select( # type: ignore
entity_0: _TScalar_0,
entity_1: Type[_TModel_1],
entity_2: Type[_TModel_2],
- **kw: Any,
) -> Select[Tuple[_TScalar_0, _TModel_1, _TModel_2]]:
...
@@ -231,7 +202,6 @@ def select( # type: ignore
entity_0: Type[_TModel_0],
entity_1: _TScalar_1,
entity_2: _TScalar_2,
- **kw: Any,
) -> Select[Tuple[_TModel_0, _TScalar_1, _TScalar_2]]:
...
@@ -241,7 +211,6 @@ def select( # type: ignore
entity_0: Type[_TModel_0],
entity_1: _TScalar_1,
entity_2: Type[_TModel_2],
- **kw: Any,
) -> Select[Tuple[_TModel_0, _TScalar_1, _TModel_2]]:
...
@@ -251,7 +220,6 @@ def select( # type: ignore
entity_0: Type[_TModel_0],
entity_1: Type[_TModel_1],
entity_2: _TScalar_2,
- **kw: Any,
) -> Select[Tuple[_TModel_0, _TModel_1, _TScalar_2]]:
...
@@ -261,7 +229,6 @@ def select( # type: ignore
entity_0: Type[_TModel_0],
entity_1: Type[_TModel_1],
entity_2: Type[_TModel_2],
- **kw: Any,
) -> Select[Tuple[_TModel_0, _TModel_1, _TModel_2]]:
...
@@ -272,7 +239,6 @@ def select( # type: ignore
entity_1: _TScalar_1,
entity_2: _TScalar_2,
entity_3: _TScalar_3,
- **kw: Any,
) -> Select[Tuple[_TScalar_0, _TScalar_1, _TScalar_2, _TScalar_3]]:
...
@@ -283,7 +249,6 @@ def select( # type: ignore
entity_1: _TScalar_1,
entity_2: _TScalar_2,
entity_3: Type[_TModel_3],
- **kw: Any,
) -> Select[Tuple[_TScalar_0, _TScalar_1, _TScalar_2, _TModel_3]]:
...
@@ -294,7 +259,6 @@ def select( # type: ignore
entity_1: _TScalar_1,
entity_2: Type[_TModel_2],
entity_3: _TScalar_3,
- **kw: Any,
) -> Select[Tuple[_TScalar_0, _TScalar_1, _TModel_2, _TScalar_3]]:
...
@@ -305,7 +269,6 @@ def select( # type: ignore
entity_1: _TScalar_1,
entity_2: Type[_TModel_2],
entity_3: Type[_TModel_3],
- **kw: Any,
) -> Select[Tuple[_TScalar_0, _TScalar_1, _TModel_2, _TModel_3]]:
...
@@ -316,7 +279,6 @@ def select( # type: ignore
entity_1: Type[_TModel_1],
entity_2: _TScalar_2,
entity_3: _TScalar_3,
- **kw: Any,
) -> Select[Tuple[_TScalar_0, _TModel_1, _TScalar_2, _TScalar_3]]:
...
@@ -327,7 +289,6 @@ def select( # type: ignore
entity_1: Type[_TModel_1],
entity_2: _TScalar_2,
entity_3: Type[_TModel_3],
- **kw: Any,
) -> Select[Tuple[_TScalar_0, _TModel_1, _TScalar_2, _TModel_3]]:
...
@@ -338,7 +299,6 @@ def select( # type: ignore
entity_1: Type[_TModel_1],
entity_2: Type[_TModel_2],
entity_3: _TScalar_3,
- **kw: Any,
) -> Select[Tuple[_TScalar_0, _TModel_1, _TModel_2, _TScalar_3]]:
...
@@ -349,7 +309,6 @@ def select( # type: ignore
entity_1: Type[_TModel_1],
entity_2: Type[_TModel_2],
entity_3: Type[_TModel_3],
- **kw: Any,
) -> Select[Tuple[_TScalar_0, _TModel_1, _TModel_2, _TModel_3]]:
...
@@ -360,7 +319,6 @@ def select( # type: ignore
entity_1: _TScalar_1,
entity_2: _TScalar_2,
entity_3: _TScalar_3,
- **kw: Any,
) -> Select[Tuple[_TModel_0, _TScalar_1, _TScalar_2, _TScalar_3]]:
...
@@ -371,7 +329,6 @@ def select( # type: ignore
entity_1: _TScalar_1,
entity_2: _TScalar_2,
entity_3: Type[_TModel_3],
- **kw: Any,
) -> Select[Tuple[_TModel_0, _TScalar_1, _TScalar_2, _TModel_3]]:
...
@@ -382,7 +339,6 @@ def select( # type: ignore
entity_1: _TScalar_1,
entity_2: Type[_TModel_2],
entity_3: _TScalar_3,
- **kw: Any,
) -> Select[Tuple[_TModel_0, _TScalar_1, _TModel_2, _TScalar_3]]:
...
@@ -393,7 +349,6 @@ def select( # type: ignore
entity_1: _TScalar_1,
entity_2: Type[_TModel_2],
entity_3: Type[_TModel_3],
- **kw: Any,
) -> Select[Tuple[_TModel_0, _TScalar_1, _TModel_2, _TModel_3]]:
...
@@ -404,7 +359,6 @@ def select( # type: ignore
entity_1: Type[_TModel_1],
entity_2: _TScalar_2,
entity_3: _TScalar_3,
- **kw: Any,
) -> Select[Tuple[_TModel_0, _TModel_1, _TScalar_2, _TScalar_3]]:
...
@@ -415,7 +369,6 @@ def select( # type: ignore
entity_1: Type[_TModel_1],
entity_2: _TScalar_2,
entity_3: Type[_TModel_3],
- **kw: Any,
) -> Select[Tuple[_TModel_0, _TModel_1, _TScalar_2, _TModel_3]]:
...
@@ -426,7 +379,6 @@ def select( # type: ignore
entity_1: Type[_TModel_1],
entity_2: Type[_TModel_2],
entity_3: _TScalar_3,
- **kw: Any,
) -> Select[Tuple[_TModel_0, _TModel_1, _TModel_2, _TScalar_3]]:
...
@@ -437,7 +389,6 @@ def select( # type: ignore
entity_1: Type[_TModel_1],
entity_2: Type[_TModel_2],
entity_3: Type[_TModel_3],
- **kw: Any,
) -> Select[Tuple[_TModel_0, _TModel_1, _TModel_2, _TModel_3]]:
...
@@ -445,14 +396,14 @@ def select( # type: ignore
# Generated overloads end
-def select(*entities: Any, **kw: Any) -> Union[Select, SelectOfScalar]: # type: ignore
+def select(*entities: Any) -> Union[Select, SelectOfScalar]: # type: ignore
if len(entities) == 1:
- return SelectOfScalar._create(*entities, **kw) # type: ignore
- return Select._create(*entities, **kw) # type: ignore
+ return SelectOfScalar(*entities) # type: ignore
+ return Select(*entities) # type: ignore
# TODO: add several @overload from Python types to SQLAlchemy equivalents
def col(column_expression: Any) -> ColumnClause: # type: ignore
if not isinstance(column_expression, (ColumnClause, Column, InstrumentedAttribute)):
raise RuntimeError(f"Not a SQLAlchemy column: {column_expression}")
- return column_expression
+ return column_expression # type: ignore
diff --git a/sqlmodel/sql/expression.py.jinja2 b/sqlmodel/sql/expression.py.jinja2
index 51f04a215d..4284543fe2 100644
--- a/sqlmodel/sql/expression.py.jinja2
+++ b/sqlmodel/sql/expression.py.jinja2
@@ -1,4 +1,3 @@
-import sys
from datetime import datetime
from typing import (
TYPE_CHECKING,
@@ -10,7 +9,6 @@ from typing import (
Type,
TypeVar,
Union,
- cast,
overload,
)
from uuid import UUID
@@ -22,36 +20,15 @@ from sqlalchemy.sql.expression import Select as _Select
_TSelect = TypeVar("_TSelect")
-# Workaround Generics incompatibility in Python 3.6
-# Ref: https://github.com/python/typing/issues/449#issuecomment-316061322
-if sys.version_info.minor >= 7:
+class Select(_Select[Tuple[_TSelect]], Generic[_TSelect]):
+ inherit_cache = True
- class Select(_Select, Generic[_TSelect]):
- inherit_cache = True
-
- # This is not comparable to sqlalchemy.sql.selectable.ScalarSelect, that has a different
- # purpose. This is the same as a normal SQLAlchemy Select class where there's only one
- # entity, so the result will be converted to a scalar by default. This way writing
- # for loops on the results will feel natural.
- class SelectOfScalar(_Select, Generic[_TSelect]):
- inherit_cache = True
-
-else:
- from typing import GenericMeta # type: ignore
-
- class GenericSelectMeta(GenericMeta, _Select.__class__): # type: ignore
- pass
-
- class _Py36Select(_Select, Generic[_TSelect], metaclass=GenericSelectMeta):
- inherit_cache = True
-
- class _Py36SelectOfScalar(_Select, Generic[_TSelect], metaclass=GenericSelectMeta):
- inherit_cache = True
-
- # Cast them for editors to work correctly, from several tricks tried, this works
- # for both VS Code and PyCharm
- Select = cast("Select", _Py36Select) # type: ignore
- SelectOfScalar = cast("SelectOfScalar", _Py36SelectOfScalar) # type: ignore
+# This is not comparable to sqlalchemy.sql.selectable.ScalarSelect, that has a different
+# purpose. This is the same as a normal SQLAlchemy Select class where there's only one
+# entity, so the result will be converted to a scalar by default. This way writing
+# for loops on the results will feel natural.
+class SelectOfScalar(_Select[Tuple[_TSelect]], Generic[_TSelect]):
+ inherit_cache = True
if TYPE_CHECKING: # pragma: no cover
@@ -59,6 +36,7 @@ if TYPE_CHECKING: # pragma: no cover
# Generated TypeVars start
+
{% for i in range(number_of_types) %}
_TScalar_{{ i }} = TypeVar(
"_TScalar_{{ i }}",
@@ -82,12 +60,12 @@ _TModel_{{ i }} = TypeVar("_TModel_{{ i }}", bound="SQLModel")
# Generated TypeVars end
@overload
-def select(entity_0: _TScalar_0, **kw: Any) -> SelectOfScalar[_TScalar_0]: # type: ignore
+def select(entity_0: _TScalar_0) -> SelectOfScalar[_TScalar_0]: # type: ignore
...
@overload
-def select(entity_0: Type[_TModel_0], **kw: Any) -> SelectOfScalar[_TModel_0]: # type: ignore
+def select(entity_0: Type[_TModel_0]) -> SelectOfScalar[_TModel_0]: # type: ignore
...
@@ -97,7 +75,7 @@ def select(entity_0: Type[_TModel_0], **kw: Any) -> SelectOfScalar[_TModel_0]:
@overload
def select( # type: ignore
- {% for arg in signature[0] %}{{ arg.name }}: {{ arg.annotation }}, {% endfor %}**kw: Any,
+ {% for arg in signature[0] %}{{ arg.name }}: {{ arg.annotation }}, {% endfor %}
) -> Select[Tuple[{%for ret in signature[1] %}{{ ret }} {% if not loop.last %}, {% endif %}{% endfor %}]]:
...
@@ -105,14 +83,15 @@ def select( # type: ignore
# Generated overloads end
-def select(*entities: Any, **kw: Any) -> Union[Select, SelectOfScalar]: # type: ignore
+
+def select(*entities: Any) -> Union[Select, SelectOfScalar]: # type: ignore
if len(entities) == 1:
- return SelectOfScalar._create(*entities, **kw) # type: ignore
- return Select._create(*entities, **kw) # type: ignore
+ return SelectOfScalar(*entities) # type: ignore
+ return Select(*entities) # type: ignore
# TODO: add several @overload from Python types to SQLAlchemy equivalents
def col(column_expression: Any) -> ColumnClause: # type: ignore
if not isinstance(column_expression, (ColumnClause, Column, InstrumentedAttribute)):
raise RuntimeError(f"Not a SQLAlchemy column: {column_expression}")
- return column_expression
+ return column_expression # type: ignore
diff --git a/sqlmodel/sql/sqltypes.py b/sqlmodel/sql/sqltypes.py
index 09b8239476..da6551b790 100644
--- a/sqlmodel/sql/sqltypes.py
+++ b/sqlmodel/sql/sqltypes.py
@@ -16,7 +16,7 @@ class AutoString(types.TypeDecorator): # type: ignore
def load_dialect_impl(self, dialect: Dialect) -> "types.TypeEngine[Any]":
impl = cast(types.String, self.impl)
if impl.length is None and dialect.name == "mysql":
- return dialect.type_descriptor(types.String(self.mysql_default_length)) # type: ignore
+ return dialect.type_descriptor(types.String(self.mysql_default_length))
return super().load_dialect_impl(dialect)
@@ -35,9 +35,9 @@ class GUID(types.TypeDecorator): # type: ignore
def load_dialect_impl(self, dialect: Dialect) -> TypeEngine: # type: ignore
if dialect.name == "postgresql":
- return dialect.type_descriptor(UUID()) # type: ignore
+ return dialect.type_descriptor(UUID())
else:
- return dialect.type_descriptor(CHAR(32)) # type: ignore
+ return dialect.type_descriptor(CHAR(32))
def process_bind_param(self, value: Any, dialect: Dialect) -> Optional[str]:
if value is None:
diff --git a/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001.py b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001.py
index cf008563f4..d05c4a2a5f 100644
--- a/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001.py
+++ b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001.py
@@ -173,8 +173,18 @@ def test_tutorial(clear_sqlmodel):
insp: Inspector = inspect(mod.engine)
indexes = insp.get_indexes(str(mod.Hero.__tablename__))
expected_indexes = [
- {"name": "ix_hero_name", "column_names": ["name"], "unique": 0},
- {"name": "ix_hero_age", "column_names": ["age"], "unique": 0},
+ {
+ "name": "ix_hero_name",
+ "column_names": ["name"],
+ "unique": 0,
+ "dialect_options": {},
+ },
+ {
+ "name": "ix_hero_age",
+ "column_names": ["age"],
+ "unique": 0,
+ "dialect_options": {},
+ },
]
for index in expected_indexes:
assert index in indexes, "This expected index should be in the indexes in DB"
diff --git a/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002.py b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002.py
index 57393a7ddc..a8b5b7b1c3 100644
--- a/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002.py
+++ b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002.py
@@ -173,8 +173,18 @@ def test_tutorial(clear_sqlmodel):
insp: Inspector = inspect(mod.engine)
indexes = insp.get_indexes(str(mod.Hero.__tablename__))
expected_indexes = [
- {"name": "ix_hero_age", "column_names": ["age"], "unique": 0},
- {"name": "ix_hero_name", "column_names": ["name"], "unique": 0},
+ {
+ "name": "ix_hero_age",
+ "column_names": ["age"],
+ "unique": 0,
+ "dialect_options": {},
+ },
+ {
+ "name": "ix_hero_name",
+ "column_names": ["name"],
+ "unique": 0,
+ "dialect_options": {},
+ },
]
for index in expected_indexes:
assert index in indexes, "This expected index should be in the indexes in DB"
diff --git a/tests/test_tutorial/test_indexes/test_tutorial001.py b/tests/test_tutorial/test_indexes/test_tutorial001.py
index 596207737d..bc89522a67 100644
--- a/tests/test_tutorial/test_indexes/test_tutorial001.py
+++ b/tests/test_tutorial/test_indexes/test_tutorial001.py
@@ -25,8 +25,18 @@ def test_tutorial(clear_sqlmodel):
insp: Inspector = inspect(mod.engine)
indexes = insp.get_indexes(str(mod.Hero.__tablename__))
expected_indexes = [
- {"name": "ix_hero_name", "column_names": ["name"], "unique": 0},
- {"name": "ix_hero_age", "column_names": ["age"], "unique": 0},
+ {
+ "name": "ix_hero_name",
+ "column_names": ["name"],
+ "unique": 0,
+ "dialect_options": {},
+ },
+ {
+ "name": "ix_hero_age",
+ "column_names": ["age"],
+ "unique": 0,
+ "dialect_options": {},
+ },
]
for index in expected_indexes:
assert index in indexes, "This expected index should be in the indexes in DB"
diff --git a/tests/test_tutorial/test_indexes/test_tutorial006.py b/tests/test_tutorial/test_indexes/test_tutorial006.py
index e26f8b2ed8..8d574dd0df 100644
--- a/tests/test_tutorial/test_indexes/test_tutorial006.py
+++ b/tests/test_tutorial/test_indexes/test_tutorial006.py
@@ -26,8 +26,18 @@ def test_tutorial(clear_sqlmodel):
insp: Inspector = inspect(mod.engine)
indexes = insp.get_indexes(str(mod.Hero.__tablename__))
expected_indexes = [
- {"name": "ix_hero_name", "column_names": ["name"], "unique": 0},
- {"name": "ix_hero_age", "column_names": ["age"], "unique": 0},
+ {
+ "name": "ix_hero_name",
+ "column_names": ["name"],
+ "unique": 0,
+ "dialect_options": {},
+ },
+ {
+ "name": "ix_hero_age",
+ "column_names": ["age"],
+ "unique": 0,
+ "dialect_options": {},
+ },
]
for index in expected_indexes:
assert index in indexes, "This expected index should be in the indexes in DB"