From 2454694de330f2e986f981397d7cef90393d573e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 29 Apr 2024 15:11:02 -0700 Subject: [PATCH 01/46] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20types=20t?= =?UTF-8?q?o=20properly=20support=20Pydantic=202.7=20(#913)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sqlmodel/_compat.py | 4 +++- sqlmodel/main.py | 20 +++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/sqlmodel/_compat.py b/sqlmodel/_compat.py index 072d2b0f58..72ec8330fd 100644 --- a/sqlmodel/_compat.py +++ b/sqlmodel/_compat.py @@ -18,11 +18,13 @@ Union, ) -from pydantic import VERSION as PYDANTIC_VERSION +from pydantic import VERSION as P_VERSION from pydantic import BaseModel from pydantic.fields import FieldInfo from typing_extensions import get_args, get_origin +# Reassign variable to make it reexported for mypy +PYDANTIC_VERSION = P_VERSION IS_PYDANTIC_V2 = PYDANTIC_VERSION.startswith("2.") diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 9e8330d69d..a16428b192 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -6,6 +6,7 @@ from enum import Enum from pathlib import Path from typing import ( + TYPE_CHECKING, AbstractSet, Any, Callable, @@ -55,6 +56,7 @@ from ._compat import ( # type: ignore[attr-defined] IS_PYDANTIC_V2, + PYDANTIC_VERSION, BaseConfig, ModelField, ModelMetaclass, @@ -80,6 +82,12 @@ ) from .sql.sqltypes import GUID, AutoString +if TYPE_CHECKING: + from pydantic._internal._model_construction import ModelMetaclass as ModelMetaclass + from pydantic._internal._repr import Representation as Representation + from pydantic_core import PydanticUndefined as Undefined + from pydantic_core import PydanticUndefinedType as UndefinedType + _T = TypeVar("_T") NoArgAnyCallable = Callable[[], Any] IncEx = Union[Set[int], Set[str], Dict[int, Any], Dict[str, Any], None] @@ -764,13 +772,22 @@ def model_dump( mode: Union[Literal["json", "python"], str] = "python", include: IncEx = None, exclude: IncEx = None, + context: Union[Dict[str, Any], None] = None, by_alias: bool = False, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, round_trip: bool = False, - warnings: bool = True, + warnings: Union[bool, Literal["none", "warn", "error"]] = True, + serialize_as_any: bool = False, ) -> Dict[str, Any]: + if PYDANTIC_VERSION >= "2.7.0": + extra_kwargs: Dict[str, Any] = { + "context": context, + "serialize_as_any": serialize_as_any, + } + else: + extra_kwargs = {} if IS_PYDANTIC_V2: return super().model_dump( mode=mode, @@ -782,6 +799,7 @@ def model_dump( exclude_none=exclude_none, round_trip=round_trip, warnings=warnings, + **extra_kwargs, ) else: return super().dict( From f67867f9741aecde2f70a09d08baf7197003dca2 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 29 Apr 2024 22:11:20 +0000 Subject: [PATCH 02/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index eba16d13a8..1fef70b248 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,10 @@ ## Latest Changes +### Refactors + +* ♻️ Refactor types to properly support Pydantic 2.7. PR [#913](https://github.com/tiangolo/sqlmodel/pull/913) by [@tiangolo](https://github.com/tiangolo). + ### Docs * 📝 Update ModelRead to ModelPublic documentation and examples. PR [#885](https://github.com/tiangolo/sqlmodel/pull/885) by [@estebanx64](https://github.com/estebanx64). From 0b4989d0b2bdc15fdf7499f62cc63198a582a9fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 29 Apr 2024 15:58:15 -0700 Subject: [PATCH 03/46] =?UTF-8?q?=F0=9F=94=A7=20Migrate=20from=20Poetry=20?= =?UTF-8?q?to=20PDM=20for=20the=20internal=20build=20config=20(#912)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-docs.yml | 20 +++----- .github/workflows/publish.yml | 42 ++++++---------- .github/workflows/test-redistribute.yml | 50 +++++++++++++++++++ .github/workflows/test.yml | 16 ++---- pyproject.toml | 65 ++++++++++++------------- requirements-docs-tests.txt | 2 + requirements-docs.txt | 18 +++++++ requirements-tests.txt | 12 +++++ requirements.txt | 6 +++ sqlmodel/__init__.py | 2 +- 10 files changed, 145 insertions(+), 88 deletions(-) create mode 100644 .github/workflows/test-redistribute.yml create mode 100644 requirements-docs-tests.txt create mode 100644 requirements-docs.txt create mode 100644 requirements-tests.txt create mode 100644 requirements.txt diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index e7de374652..4871f7bb47 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -27,6 +27,7 @@ jobs: - README.md - docs/** - docs_src/** + - requirements-docs.txt - pyproject.toml - mkdocs.yml - mkdocs.insiders.yml @@ -52,21 +53,16 @@ jobs: id: cache with: path: ${{ env.pythonLocation }} - key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-v01 - - name: Install Poetry + key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-docs.txt') }}-v01 + - name: Install docs extras if: steps.cache.outputs.cache-hit != 'true' - run: | - python -m pip install --upgrade pip - python -m pip install "poetry" - python -m poetry self add poetry-version-plugin - - name: Configure poetry - run: python -m poetry config virtualenvs.create false - - name: Install Dependencies - if: steps.cache.outputs.cache-hit != 'true' - run: python -m poetry install + run: pip install -r requirements-docs.txt - name: Install Material for MkDocs Insiders if: ( github.event_name != 'pull_request' || github.secret_source == 'Actions' ) && steps.cache.outputs.cache-hit != 'true' - run: python -m poetry run pip install git+https://${{ secrets.SQLMODEL_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git + run: | + pip install git+https://${{ secrets.SQLMODEL_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git + pip install git+https://${{ secrets.SQLMODEL_MKDOCS_MATERIAL_INSIDERS }}@github.com/pawamoy-insiders/griffe-typing-deprecated.git + pip install git+https://${{ secrets.SQLMODEL_MKDOCS_MATERIAL_INSIDERS }}@github.com/pawamoy-insiders/mkdocstrings-python.git - uses: actions/cache@v3 with: key: mkdocs-cards-${{ github.ref }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 42a0eec3ce..911cd623a7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,37 +14,23 @@ on: jobs: publish: runs-on: ubuntu-latest + strategy: + matrix: + package: + - sqlmodel + permissions: + id-token: write steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: - python-version: "3.7" - # Allow debugging with tmate - - name: Setup tmate session - uses: mxschmitt/action-tmate@v3 - if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }} - with: - limit-access-to-actor: true - - uses: actions/cache@v3 - id: cache - with: - path: ${{ env.pythonLocation }} - key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root-v2 - - name: Install poetry - if: steps.cache.outputs.cache-hit != 'true' - run: | - python -m pip install --upgrade pip - python -m pip install "poetry" - python -m poetry self add poetry-version-plugin - - name: Configure poetry - run: python -m poetry config virtualenvs.create false - - name: Install Dependencies - if: steps.cache.outputs.cache-hit != 'true' - run: python -m poetry install - - name: Publish + python-version: "3.11" + - name: Install build dependencies + run: pip install build + - name: Build distribution env: - PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} - run: | - python -m poetry config pypi-token.pypi $PYPI_TOKEN - bash scripts/publish.sh + TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }} + run: python -m build + - name: Publish + uses: pypa/gh-action-pypi-publish@v1.8.11 diff --git a/.github/workflows/test-redistribute.yml b/.github/workflows/test-redistribute.yml new file mode 100644 index 0000000000..45b82414c4 --- /dev/null +++ b/.github/workflows/test-redistribute.yml @@ -0,0 +1,50 @@ +name: Test Redistribute + +on: + push: + branches: + - main + pull_request: + types: + - opened + - synchronize + +jobs: + test-redistribute: + runs-on: ubuntu-latest + strategy: + matrix: + package: + - sqlmodel + steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.10" + - name: Install build dependencies + run: pip install build + - name: Build source distribution + env: + TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }} + run: python -m build --sdist + - name: Decompress source distribution + run: | + cd dist + tar xvf sqlmodel*.tar.gz + - name: Install test dependencies + run: | + cd dist/sqlmodel*/ + pip install -r requirements-tests.txt + - name: Run source distribution tests + run: | + cd dist/sqlmodel*/ + bash scripts/test.sh + - name: Build wheel distribution + run: | + cd dist + pip wheel --no-deps sqlmodel*.tar.gz diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 990bf46de4..f0872ef623 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -51,18 +51,10 @@ jobs: id: cache with: path: ${{ env.pythonLocation }} - key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root-v2 - - name: Install poetry - if: steps.cache.outputs.cache-hit != 'true' - run: | - python -m pip install --upgrade pip - python -m pip install "poetry" - python -m poetry self add poetry-version-plugin - - name: Configure poetry - run: python -m poetry config virtualenvs.create false + key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }} - name: Install Dependencies if: steps.cache.outputs.cache-hit != 'true' - run: python -m poetry install + run: pip install -r requirements-tests.txt - name: Install Pydantic v1 if: matrix.pydantic-version == 'pydantic-v1' run: pip install --upgrade "pydantic>=1.10.0,<2.0.0" @@ -72,10 +64,10 @@ jobs: - name: Lint # Do not run on Python 3.7 as mypy behaves differently if: matrix.python-version != '3.7' && matrix.pydantic-version == 'pydantic-v2' - run: python -m poetry run bash scripts/lint.sh + run: bash scripts/lint.sh - run: mkdir coverage - name: Test - run: python -m poetry run bash scripts/test.sh + run: bash scripts/test.sh env: COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }} CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }} diff --git a/pyproject.toml b/pyproject.toml index 9da631b985..17f72941b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,14 +1,17 @@ -[tool.poetry] +[build-system] +requires = ["pdm-backend"] +build-backend = "pdm.backend" + +[project] name = "sqlmodel" -version = "0" +dynamic = ["version"] description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness." -authors = ["Sebastián Ramírez "] readme = "README.md" -homepage = "https://github.com/tiangolo/sqlmodel" -documentation = "https://sqlmodel.tiangolo.com" -repository = "https://github.com/tiangolo/sqlmodel" -license = "MIT" -exclude = ["sqlmodel/sql/expression.py.jinja2"] +requires-python = ">=3.7" +authors = [ + { name = "Sebastián Ramírez", email = "tiangolo@gmail.com" }, +] + classifiers = [ "Development Status :: 4 - Beta", "Framework :: AsyncIO", @@ -31,36 +34,28 @@ classifiers = [ "Typing :: Typed", ] -[tool.poetry.dependencies] -python = "^3.7" -SQLAlchemy = ">=2.0.0,<2.1.0" -pydantic = ">=1.10.13,<3.0.0" +dependencies = [ + "SQLAlchemy >=2.0.0,<2.1.0", + "pydantic >=1.10.13,<3.0.0", +] -[tool.poetry.group.dev.dependencies] -pytest = "^7.0.1" -mypy = "1.4.1" -# Needed by the code generator using templates -black = ">=22.10,<24.0" -mkdocs-material = "9.2.7" -pillow = "^9.3.0" -cairosvg = "^2.5.2" -mdx-include = "^1.4.1" -coverage = {extras = ["toml"], version = ">=6.2,<8.0"} -fastapi = "^0.103.2" -ruff = "0.2.0" -# For FastAPI tests -httpx = "0.24.1" -# TODO: upgrade when deprecating Python 3.7 -dirty-equals = "^0.6.0" -typer-cli = "^0.0.13" -mkdocs-markdownextradata-plugin = ">=0.1.7,<0.3.0" +[project.urls] +Homepage = "https://github.com/tiangolo/sqlmodel" +Documentation = "https://sqlmodel.tiangolo.com" +Repository = "https://github.com/tiangolo/sqlmodel" -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" +[tool.pdm] +version = { source = "file", path = "sqlmodel/__init__.py" } +distribution = true -[tool.poetry-version-plugin] -source = "init" +[tool.pdm.build] +source-includes = [ + "tests/", + "docs_src/", + "requirements*.txt", + "scripts/", + "sqlmodel/sql/expression.py.jinja2", + ] [tool.coverage.run] parallel = true diff --git a/requirements-docs-tests.txt b/requirements-docs-tests.txt new file mode 100644 index 0000000000..28f1ad1be9 --- /dev/null +++ b/requirements-docs-tests.txt @@ -0,0 +1,2 @@ +# For mkdocstrings and code generator using templates +black >=22.10,<24.0 diff --git a/requirements-docs.txt b/requirements-docs.txt new file mode 100644 index 0000000000..cacb5dc2a3 --- /dev/null +++ b/requirements-docs.txt @@ -0,0 +1,18 @@ +-e . +-r requirements-docs-tests.txt +mkdocs-material==9.4.7 +mdx-include >=1.4.1,<2.0.0 +mkdocs-markdownextradata-plugin >=0.1.7,<0.3.0 +mkdocs-redirects>=1.2.1,<1.3.0 +pyyaml >=5.3.1,<7.0.0 +# For Material for MkDocs, Chinese search +jieba==0.42.1 +# For image processing by Material for MkDocs +pillow==10.1.0 +# For image processing by Material for MkDocs +cairosvg==2.7.0 +mkdocstrings[python]==0.23.0 +griffe-typingdoc==0.2.2 +# For griffe, it formats with black +black==23.3.0 +typer == 0.12.3 diff --git a/requirements-tests.txt b/requirements-tests.txt new file mode 100644 index 0000000000..648f99b1c9 --- /dev/null +++ b/requirements-tests.txt @@ -0,0 +1,12 @@ +-e . +-r requirements-docs-tests.txt +pytest >=7.0.1,<8.0.0 +coverage[toml] >=6.2,<8.0 +mypy ==1.4.1 +ruff ==0.2.0 +# For FastAPI tests +fastapi >=0.103.2 +httpx ==0.24.1 +# TODO: upgrade when deprecating Python 3.7 +dirty-equals ==0.6.0 +jinja2 ==3.1.3 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000..1e21c5d2f3 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +-e . + +-r requirements-tests.txt +-r requirements-docs.txt + +pre-commit >=2.17.0,<4.0.0 diff --git a/sqlmodel/__init__.py b/sqlmodel/__init__.py index 556bba1895..fac039e819 100644 --- a/sqlmodel/__init__.py +++ b/sqlmodel/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.0.16" +__version__ = "0.0.17.dev2" # Re-export from SQLAlchemy from sqlalchemy.engine import create_engine as create_engine From 570cd9f10c6bed37b1a674ff7e99a0ee2b962c2d Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 29 Apr 2024 22:58:36 +0000 Subject: [PATCH 04/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 1fef70b248..b55bdcdc8e 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ ### Internal +* 🔧 Migrate from Poetry to PDM for the internal build config. PR [#912](https://github.com/tiangolo/sqlmodel/pull/912) by [@tiangolo](https://github.com/tiangolo). * 🔧 Update MkDocs, disable cards while I can upgrade to the latest MkDocs Material, that fixes an issue with social cards. PR [#888](https://github.com/tiangolo/sqlmodel/pull/888) by [@tiangolo](https://github.com/tiangolo). * 👷 Add cron to run test once a week on monday. PR [#869](https://github.com/tiangolo/sqlmodel/pull/869) by [@estebanx64](https://github.com/estebanx64). * ⬆️ Upgrade Ruff version and configs. PR [#859](https://github.com/tiangolo/sqlmodel/pull/859) by [@tiangolo](https://github.com/tiangolo). From 7023896d7c25bf99aade92e7e2c2a04db250b1ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 29 Apr 2024 16:24:50 -0700 Subject: [PATCH 05/46] =?UTF-8?q?=F0=9F=94=A8=20Update=20internal=20script?= =?UTF-8?q?s=20and=20remove=20unused=20ones=20(#914)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .flake8 | 5 ----- scripts/docs-live.sh | 7 ------- scripts/format.sh | 2 +- scripts/lint.sh | 2 +- scripts/publish.sh | 5 ----- scripts/test-files.sh | 7 ------- scripts/zip-docs.sh | 11 ----------- 7 files changed, 2 insertions(+), 37 deletions(-) delete mode 100644 .flake8 delete mode 100755 scripts/docs-live.sh delete mode 100755 scripts/publish.sh delete mode 100755 scripts/test-files.sh delete mode 100644 scripts/zip-docs.sh diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 47ef7c07fc..0000000000 --- a/.flake8 +++ /dev/null @@ -1,5 +0,0 @@ -[flake8] -max-line-length = 88 -select = C,E,F,W,B,B9 -ignore = E203, E501, W503 -exclude = __init__.py diff --git a/scripts/docs-live.sh b/scripts/docs-live.sh deleted file mode 100755 index b6071fdb97..0000000000 --- a/scripts/docs-live.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -set -e - -export DYLD_FALLBACK_LIBRARY_PATH="/opt/homebrew/lib" - -LINENUMS="true" mkdocs serve --dev-addr 127.0.0.1:8008 diff --git a/scripts/format.sh b/scripts/format.sh index 70c12e579d..8414381583 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -1,5 +1,5 @@ #!/bin/sh -e set -x -ruff sqlmodel tests docs_src scripts --fix +ruff check sqlmodel tests docs_src scripts --fix ruff format sqlmodel tests docs_src scripts diff --git a/scripts/lint.sh b/scripts/lint.sh index f66882239f..4b279b5fcb 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -4,5 +4,5 @@ set -e set -x mypy sqlmodel -ruff sqlmodel tests docs_src scripts +ruff check sqlmodel tests docs_src scripts ruff format sqlmodel tests docs_src --check diff --git a/scripts/publish.sh b/scripts/publish.sh deleted file mode 100755 index 7a9a127101..0000000000 --- a/scripts/publish.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -set -e - -python -m poetry publish --build diff --git a/scripts/test-files.sh b/scripts/test-files.sh deleted file mode 100755 index 36579ce7b6..0000000000 --- a/scripts/test-files.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -set -e -set -x - -# Check README.md is up to date -diff --brief docs/index.md README.md diff --git a/scripts/zip-docs.sh b/scripts/zip-docs.sh deleted file mode 100644 index 69315f5ddd..0000000000 --- a/scripts/zip-docs.sh +++ /dev/null @@ -1,11 +0,0 @@ -#! /usr/bin/env bash - -set -x -set -e - -cd ./site - -if [ -f docs.zip ]; then - rm -rf docs.zip -fi -zip -r docs.zip ./ From 32353eeb074bb7c1e50286a6f49e9780196c9e56 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 29 Apr 2024 23:26:22 +0000 Subject: [PATCH 06/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index b55bdcdc8e..e64cc01553 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ ### Internal +* 🔨 Update internal scripts and remove unused ones. PR [#914](https://github.com/tiangolo/sqlmodel/pull/914) by [@tiangolo](https://github.com/tiangolo). * 🔧 Migrate from Poetry to PDM for the internal build config. PR [#912](https://github.com/tiangolo/sqlmodel/pull/912) by [@tiangolo](https://github.com/tiangolo). * 🔧 Update MkDocs, disable cards while I can upgrade to the latest MkDocs Material, that fixes an issue with social cards. PR [#888](https://github.com/tiangolo/sqlmodel/pull/888) by [@tiangolo](https://github.com/tiangolo). * 👷 Add cron to run test once a week on monday. PR [#869](https://github.com/tiangolo/sqlmodel/pull/869) by [@estebanx64](https://github.com/estebanx64). From 3ecbeacb469fc4ff7cdfd7bfad78df6a3764427b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 23:34:38 +0000 Subject: [PATCH 07/46] =?UTF-8?q?=E2=AC=86=20Bump=20actions/setup-python?= =?UTF-8?q?=20from=204=20to=205=20(#733)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sebastián Ramírez --- .github/workflows/build-docs.yml | 2 +- .github/workflows/publish.yml | 2 +- .github/workflows/smokeshow.yml | 2 +- .github/workflows/test.yml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 4871f7bb47..b9e1d336ac 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -46,7 +46,7 @@ jobs: run: echo "$GITHUB_CONTEXT" - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.11" - uses: actions/cache@v3 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 911cd623a7..6f57b7f8ef 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,7 +23,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.11" - name: Install build dependencies diff --git a/.github/workflows/smokeshow.yml b/.github/workflows/smokeshow.yml index d2c274ff29..44ce46cfe8 100644 --- a/.github/workflows/smokeshow.yml +++ b/.github/workflows/smokeshow.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.9' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f0872ef623..59260fa7b0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,7 +38,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} # Allow debugging with tmate @@ -84,7 +84,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.8' From 1c4f425f1744e34ad425d061668b818673f54a43 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 29 Apr 2024 23:35:03 +0000 Subject: [PATCH 08/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index e64cc01553..bec99c83fb 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ ### Internal +* ⬆ Bump actions/setup-python from 4 to 5. PR [#733](https://github.com/tiangolo/sqlmodel/pull/733) by [@dependabot[bot]](https://github.com/apps/dependabot). * 🔨 Update internal scripts and remove unused ones. PR [#914](https://github.com/tiangolo/sqlmodel/pull/914) by [@tiangolo](https://github.com/tiangolo). * 🔧 Migrate from Poetry to PDM for the internal build config. PR [#912](https://github.com/tiangolo/sqlmodel/pull/912) by [@tiangolo](https://github.com/tiangolo). * 🔧 Update MkDocs, disable cards while I can upgrade to the latest MkDocs Material, that fixes an issue with social cards. PR [#888](https://github.com/tiangolo/sqlmodel/pull/888) by [@tiangolo](https://github.com/tiangolo). From 0431c5bb26b587a99cd0aa0efbf81e62e6f6d0d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 29 Apr 2024 16:44:21 -0700 Subject: [PATCH 09/46] =?UTF-8?q?=F0=9F=94=96=20Release=20version=200.0.17?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 2 ++ sqlmodel/__init__.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index bec99c83fb..da824db5dd 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,8 @@ ## Latest Changes +## 0.0.17 + ### Refactors * ♻️ Refactor types to properly support Pydantic 2.7. PR [#913](https://github.com/tiangolo/sqlmodel/pull/913) by [@tiangolo](https://github.com/tiangolo). diff --git a/sqlmodel/__init__.py b/sqlmodel/__init__.py index fac039e819..aa108fefe9 100644 --- a/sqlmodel/__init__.py +++ b/sqlmodel/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.0.17.dev2" +__version__ = "0.0.17" # Re-export from SQLAlchemy from sqlalchemy.engine import create_engine as create_engine From 28d0e7637039f49553f081add72500939ad6d86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 29 Apr 2024 17:00:40 -0700 Subject: [PATCH 10/46] =?UTF-8?q?=F0=9F=94=A7=20Re-enable=20MkDocs=20Mater?= =?UTF-8?q?ial=20Social=20plugin=20(#915)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mkdocs.insiders.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mkdocs.insiders.yml b/mkdocs.insiders.yml index f59d3f3327..d24d754930 100644 --- a/mkdocs.insiders.yml +++ b/mkdocs.insiders.yml @@ -1,4 +1,3 @@ plugins: - # TODO: Re-enable once this is fixed: https://github.com/squidfunk/mkdocs-material/issues/6983 - # social: + social: typeset: From 39cbf2790457b84c3f8abd58e95d7e79a7652e20 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 30 Apr 2024 00:01:02 +0000 Subject: [PATCH 11/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index da824db5dd..48e3eba7ce 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,10 @@ ## Latest Changes +### Internal + +* 🔧 Re-enable MkDocs Material Social plugin. PR [#915](https://github.com/tiangolo/sqlmodel/pull/915) by [@tiangolo](https://github.com/tiangolo). + ## 0.0.17 ### Refactors From 9ebbf255f71ba4dc2f17b22443e7a87d3da33b36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 29 Apr 2024 23:22:28 -0700 Subject: [PATCH 12/46] =?UTF-8?q?=E2=9C=A8=20Add=20sqlmodel-slim=20setup?= =?UTF-8?q?=20(#916)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/publish.yml | 1 + .github/workflows/test-redistribute.yml | 1 + pdm_build.py | 39 +++++++++++++++++++++++++ pyproject.toml | 12 ++++++++ sqlmodel/__init__.py | 2 +- 5 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 pdm_build.py diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6f57b7f8ef..1397e17ae6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,6 +18,7 @@ jobs: matrix: package: - sqlmodel + - sqlmodel-slim permissions: id-token: write steps: diff --git a/.github/workflows/test-redistribute.yml b/.github/workflows/test-redistribute.yml index 45b82414c4..2994da2fe5 100644 --- a/.github/workflows/test-redistribute.yml +++ b/.github/workflows/test-redistribute.yml @@ -16,6 +16,7 @@ jobs: matrix: package: - sqlmodel + - sqlmodel-slim steps: - name: Dump GitHub context env: diff --git a/pdm_build.py b/pdm_build.py new file mode 100644 index 0000000000..2324670159 --- /dev/null +++ b/pdm_build.py @@ -0,0 +1,39 @@ +import os +from typing import Any, Dict, List + +from pdm.backend.hooks import Context + +TIANGOLO_BUILD_PACKAGE = os.getenv("TIANGOLO_BUILD_PACKAGE", "sqlmodel") + + +def pdm_build_initialize(context: Context) -> None: + metadata = context.config.metadata + # Get custom config for the current package, from the env var + config: Dict[str, Any] = context.config.data["tool"]["tiangolo"][ + "_internal-slim-build" + ]["packages"][TIANGOLO_BUILD_PACKAGE] + project_config: Dict[str, Any] = config["project"] + # Get main optional dependencies, extras + optional_dependencies: Dict[str, List[str]] = metadata.get( + "optional-dependencies", {} + ) + # Get custom optional dependencies name to always include in this (non-slim) package + include_optional_dependencies: List[str] = config.get( + "include-optional-dependencies", [] + ) + # Override main [project] configs with custom configs for this package + for key, value in project_config.items(): + metadata[key] = value + # Get custom build config for the current package + build_config: Dict[str, Any] = ( + config.get("tool", {}).get("pdm", {}).get("build", {}) + ) + # Override PDM build config with custom build config for this package + for key, value in build_config.items(): + context.config.build_config[key] = value + # Get main dependencies + dependencies: List[str] = metadata.get("dependencies", []) + # Add optional dependencies to the default dependencies for this (non-slim) package + for include_optional in include_optional_dependencies: + optional_dependencies_group = optional_dependencies.get(include_optional, []) + dependencies.extend(optional_dependencies_group) diff --git a/pyproject.toml b/pyproject.toml index 17f72941b8..ade520ab48 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,6 +57,18 @@ source-includes = [ "sqlmodel/sql/expression.py.jinja2", ] +[tool.tiangolo._internal-slim-build.packages.sqlmodel-slim.project] +name = "sqlmodel-slim" + +[tool.tiangolo._internal-slim-build.packages.sqlmodel] +# include-optional-dependencies = ["standard"] + +[tool.tiangolo._internal-slim-build.packages.sqlmodel.project] +optional-dependencies = {} + +# [tool.tiangolo._internal-slim-build.packages.sqlmodel.project.scripts] +# sqlmodel = "sqlmodel.cli:main" + [tool.coverage.run] parallel = true source = [ diff --git a/sqlmodel/__init__.py b/sqlmodel/__init__.py index aa108fefe9..39a8d46418 100644 --- a/sqlmodel/__init__.py +++ b/sqlmodel/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.0.17" +__version__ = "0.0.18.dev1" # Re-export from SQLAlchemy from sqlalchemy.engine import create_engine as create_engine From a280b58c1051511d1dbbac90ad5cb02bf8121caf Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 30 Apr 2024 06:22:46 +0000 Subject: [PATCH 13/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 48e3eba7ce..ddede18e0e 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -4,6 +4,7 @@ ### Internal +* ✨ Add sqlmodel-slim setup. PR [#916](https://github.com/tiangolo/sqlmodel/pull/916) by [@tiangolo](https://github.com/tiangolo). * 🔧 Re-enable MkDocs Material Social plugin. PR [#915](https://github.com/tiangolo/sqlmodel/pull/915) by [@tiangolo](https://github.com/tiangolo). ## 0.0.17 From 900e0d3371dd8d1e2a8d5665c4255ca7d9625896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 29 Apr 2024 23:25:02 -0700 Subject: [PATCH 14/46] =?UTF-8?q?=F0=9F=94=96=20Release=20version=200.0.18?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 2 ++ sqlmodel/__init__.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index ddede18e0e..3235142562 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,8 @@ ## Latest Changes +## 0.0.18 + ### Internal * ✨ Add sqlmodel-slim setup. PR [#916](https://github.com/tiangolo/sqlmodel/pull/916) by [@tiangolo](https://github.com/tiangolo). diff --git a/sqlmodel/__init__.py b/sqlmodel/__init__.py index 39a8d46418..397c07f5d2 100644 --- a/sqlmodel/__init__.py +++ b/sqlmodel/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.0.18.dev1" +__version__ = "0.0.18" # Re-export from SQLAlchemy from sqlalchemy.engine import create_engine as create_engine From c13b71056e10dc183f6837acb3204fb179cc4b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 29 Apr 2024 23:29:21 -0700 Subject: [PATCH 15/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 3235142562..5f62c34633 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -6,7 +6,10 @@ ### Internal -* ✨ Add sqlmodel-slim setup. PR [#916](https://github.com/tiangolo/sqlmodel/pull/916) by [@tiangolo](https://github.com/tiangolo). +* ✨ Add `sqlmodel-slim` setup. PR [#916](https://github.com/tiangolo/sqlmodel/pull/916) by [@tiangolo](https://github.com/tiangolo). + +In the future SQLModel will include the standard default recommended packages, and `sqlmodel-slim` will come without those recommended standard packages and with a group of optional dependencies `sqlmodel-slim[standard]`, equivalent to `sqlmodel`, for those that want to opt out of those packages. + * 🔧 Re-enable MkDocs Material Social plugin. PR [#915](https://github.com/tiangolo/sqlmodel/pull/915) by [@tiangolo](https://github.com/tiangolo). ## 0.0.17 From 5e592c9a0d59d8c08f7dc25bc7617dc195bc1cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 7 May 2024 11:32:16 -0700 Subject: [PATCH 16/46] =?UTF-8?q?=F0=9F=91=B7=20Tweak=20CI=20for=20test-re?= =?UTF-8?q?distribute,=20add=20needed=20env=20vars=20for=20slim=20(#929)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test-redistribute.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test-redistribute.yml b/.github/workflows/test-redistribute.yml index 2994da2fe5..dc86da69c0 100644 --- a/.github/workflows/test-redistribute.yml +++ b/.github/workflows/test-redistribute.yml @@ -41,6 +41,8 @@ jobs: run: | cd dist/sqlmodel*/ pip install -r requirements-tests.txt + env: + TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }} - name: Run source distribution tests run: | cd dist/sqlmodel*/ From df0f834227cff41f18eba35db6a242a42671e893 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 7 May 2024 18:32:44 +0000 Subject: [PATCH 17/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 5f62c34633..9c14895b34 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,10 @@ ## Latest Changes +### Internal + +* 👷 Tweak CI for test-redistribute, add needed env vars for slim. PR [#929](https://github.com/tiangolo/sqlmodel/pull/929) by [@tiangolo](https://github.com/tiangolo). + ## 0.0.18 ### Internal From e4013acc548f809001179fd48192052e927547a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 10 May 2024 14:00:24 -0700 Subject: [PATCH 18/46] =?UTF-8?q?=F0=9F=91=B7=20Update=20GitHub=20Actions?= =?UTF-8?q?=20to=20download=20and=20upload=20artifacts=20(#936)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-docs.yml | 2 +- .github/workflows/deploy-docs.yml | 16 +++++++--------- .github/workflows/smokeshow.yml | 10 ++++++---- .github/workflows/test.yml | 11 ++++++----- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index b9e1d336ac..16da4a2da3 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -71,7 +71,7 @@ jobs: run: python ./scripts/docs.py verify-readme - name: Build Docs run: python ./scripts/docs.py build - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: docs-site path: ./site/** diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index f9035d89a7..41320b57b1 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -19,18 +19,16 @@ jobs: run: | rm -rf ./site mkdir ./site - - name: Download Artifact Docs - id: download - uses: dawidd6/action-download-artifact@v2.28.0 + - uses: actions/download-artifact@v4 with: - if_no_artifact_found: ignore - github_token: ${{ secrets.GITHUB_TOKEN }} - workflow: build-docs.yml - run_id: ${{ github.event.workflow_run.id }} - name: docs-site path: ./site/ + pattern: docs-site + merge-multiple: true + github-token: ${{ secrets.GITHUB_TOKEN }} + run-id: ${{ github.event.workflow_run.id }} - name: Deploy to Cloudflare Pages - if: steps.download.outputs.found_artifact == 'true' + # hashFiles returns an empty string if there are no files + if: hashFiles('./site/*') id: deploy uses: cloudflare/pages-action@v1 with: diff --git a/.github/workflows/smokeshow.yml b/.github/workflows/smokeshow.yml index 44ce46cfe8..bc37a92e78 100644 --- a/.github/workflows/smokeshow.yml +++ b/.github/workflows/smokeshow.yml @@ -20,12 +20,14 @@ jobs: - run: pip install smokeshow - - uses: dawidd6/action-download-artifact@v2.28.0 + - uses: actions/download-artifact@v4 with: - workflow: test.yml - commit: ${{ github.event.workflow_run.head_sha }} + name: coverage-html + path: htmlcov + github-token: ${{ secrets.GITHUB_TOKEN }} + run-id: ${{ github.event.workflow_run.id }} - - run: smokeshow upload coverage-html + - run: smokeshow upload htmlcov env: SMOKESHOW_GITHUB_STATUS_DESCRIPTION: Coverage {coverage-percentage} SMOKESHOW_GITHUB_COVERAGE_THRESHOLD: 95 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 59260fa7b0..70e64094a4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -72,9 +72,9 @@ jobs: COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }} CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }} - name: Store coverage files - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: coverage + name: coverage-${{ matrix.python-version }}-${{ matrix.pydantic-version }} path: coverage coverage-combine: needs: @@ -89,10 +89,11 @@ jobs: python-version: '3.8' - name: Get coverage files - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: - name: coverage + pattern: coverage-* path: coverage + merge-multiple: true - run: pip install coverage[toml] @@ -102,7 +103,7 @@ jobs: - run: coverage html --show-contexts --title "Coverage for ${{ github.sha }}" - name: Store coverage HTML - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: coverage-html path: htmlcov From dcf4f58e8176df0200ad79a9efc3b75d29ebb9a9 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 10 May 2024 21:00:41 +0000 Subject: [PATCH 19/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 9c14895b34..5b7d2b7e3c 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -4,6 +4,7 @@ ### Internal +* 👷 Update GitHub Actions to download and upload artifacts. PR [#936](https://github.com/tiangolo/sqlmodel/pull/936) by [@tiangolo](https://github.com/tiangolo). * 👷 Tweak CI for test-redistribute, add needed env vars for slim. PR [#929](https://github.com/tiangolo/sqlmodel/pull/929) by [@tiangolo](https://github.com/tiangolo). ## 0.0.18 From 662bd641b821b6724763f8503b03481459b6e1cf Mon Sep 17 00:00:00 2001 From: Soof Golan <83900570+soof-golan@users.noreply.github.com> Date: Tue, 4 Jun 2024 02:56:30 +0300 Subject: [PATCH 20/46] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20broken=20link?= =?UTF-8?q?=20to=20`@dataclass=5Ftransform`=20(now=20PEP=20681)=20in=20`do?= =?UTF-8?q?cs/features.md`=20(#753)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastián Ramírez Co-authored-by: Patrick Arminio --- docs/features.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/docs/features.md b/docs/features.md index f84606b9b5..f0d56925c7 100644 --- a/docs/features.md +++ b/docs/features.md @@ -36,20 +36,10 @@ You will get completion for everything while writing the **minimum** amount of c You won't need to keep guessing the types of different attributes in your models, if they could be `None`, etc. Your editor will be able to help you with everything because **SQLModel** is based on **standard Python type annotations**. -**SQLModel** even adopts currently in development standards for Python type annotations to ensure the **best developer experience**, so you will get inline errors and autocompletion even while creating new model instances. +**SQLModel** adopts PEP 681 for Python type annotations to ensure the **best developer experience**, so you will get inline errors and autocompletion even while creating new model instances. -/// info - -Don't worry, adopting this in-development standard only affects/improves editor support. - -It doesn't affect performance or correctness. And if the in-progress standard was deprecated your code won't be affected. - -Meanwhile, you will get inline errors (like type checks) and autocompletion on places you wouldn't get with any other library. 🎉 - -/// - ## Short **SQLModel** has **sensible defaults** for everything, with **optional configurations** everywhere. From a319952be1cd80ba4a9e0ccc0d74cd1279aa59f4 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 3 Jun 2024 23:56:49 +0000 Subject: [PATCH 21/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 5b7d2b7e3c..41b389bffd 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,10 @@ ## Latest Changes +### Docs + +* ✏️ Fix broken link to `@dataclass_transform` (now PEP 681) in `docs/features.md`. PR [#753](https://github.com/tiangolo/sqlmodel/pull/753) by [@soof-golan](https://github.com/soof-golan). + ### Internal * 👷 Update GitHub Actions to download and upload artifacts. PR [#936](https://github.com/tiangolo/sqlmodel/pull/936) by [@tiangolo](https://github.com/tiangolo). From 5bb4cffd4984647a2cba12ff94c5a1b9b577cf69 Mon Sep 17 00:00:00 2001 From: Esteban Maya Date: Mon, 3 Jun 2024 19:39:23 -0500 Subject: [PATCH 22/46] =?UTF-8?q?=F0=9F=90=9B=20Fix=20set=20varchar=20limi?= =?UTF-8?q?t=20when=20`max=5Flength`=20is=20set=20on=20Pydantic=20models?= =?UTF-8?q?=20using=20Pydantic=20v2=20(#963)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastián Ramírez --- sqlmodel/_compat.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sqlmodel/_compat.py b/sqlmodel/_compat.py index 72ec8330fd..d42a62429e 100644 --- a/sqlmodel/_compat.py +++ b/sqlmodel/_compat.py @@ -72,6 +72,7 @@ def partial_init() -> Generator[None, None, None]: if IS_PYDANTIC_V2: + from annotated_types import MaxLen from pydantic import ConfigDict as BaseConfig from pydantic._internal._fields import PydanticMetadata from pydantic._internal._model_construction import ModelMetaclass @@ -201,7 +202,7 @@ def get_type_from_field(field: Any) -> Any: def get_field_metadata(field: Any) -> Any: for meta in field.metadata: - if isinstance(meta, PydanticMetadata): + if isinstance(meta, (PydanticMetadata, MaxLen)): return meta return FakeMetadata() From 866d9ecb29b0913f36223aeed476d3b6fe99cde9 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 4 Jun 2024 00:39:40 +0000 Subject: [PATCH 23/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 41b389bffd..9b97f7ba88 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,10 @@ ## Latest Changes +### Fixes + +* 🐛 Fix set varchar limit when `max_length` is set on Pydantic models using Pydantic v2. PR [#963](https://github.com/tiangolo/sqlmodel/pull/963) by [@estebanx64](https://github.com/estebanx64). + ### Docs * ✏️ Fix broken link to `@dataclass_transform` (now PEP 681) in `docs/features.md`. PR [#753](https://github.com/tiangolo/sqlmodel/pull/753) by [@soof-golan](https://github.com/soof-golan). From 1b275bd6a784a99f467c702ad35e551513e2aa4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 3 Jun 2024 20:34:21 -0500 Subject: [PATCH 24/46] =?UTF-8?q?=F0=9F=93=8C=20Pin=20typing-extensions=20?= =?UTF-8?q?in=20tests=20for=20compatiblity=20with=20Python=203.8,=20dirty-?= =?UTF-8?q?equals,=20Pydantic=20(#965)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test.yml | 4 ++-- requirements-tests.txt | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 70e64094a4..a1c9b36e1c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -51,7 +51,7 @@ jobs: id: cache with: path: ${{ env.pythonLocation }} - key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }} + key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-v01 - name: Install Dependencies if: steps.cache.outputs.cache-hit != 'true' run: pip install -r requirements-tests.txt @@ -60,7 +60,7 @@ jobs: run: pip install --upgrade "pydantic>=1.10.0,<2.0.0" - name: Install Pydantic v2 if: matrix.pydantic-version == 'pydantic-v2' - run: pip install --upgrade "pydantic>=2.0.2,<3.0.0" + run: pip install --upgrade "pydantic>=2.0.2,<3.0.0" "typing-extensions==4.6.1" - name: Lint # Do not run on Python 3.7 as mypy behaves differently if: matrix.python-version != '3.7' && matrix.pydantic-version == 'pydantic-v2' diff --git a/requirements-tests.txt b/requirements-tests.txt index 648f99b1c9..3c2578e9c5 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -10,3 +10,6 @@ httpx ==0.24.1 # TODO: upgrade when deprecating Python 3.7 dirty-equals ==0.6.0 jinja2 ==3.1.3 +# Pin typing-extensions until Python 3.8 is deprecated or the issue with dirty-equals +# is fixed, maybe fixed after dropping Python 3.7 and upgrading dirty-equals +typing-extensions ==4.6.1 From 71de44daba6201a653211aef60342f5c9834d6d8 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 4 Jun 2024 01:34:41 +0000 Subject: [PATCH 25/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 9b97f7ba88..de348cf2d7 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -12,6 +12,7 @@ ### Internal +* 📌 Pin typing-extensions in tests for compatiblity with Python 3.8, dirty-equals, Pydantic. PR [#965](https://github.com/tiangolo/sqlmodel/pull/965) by [@tiangolo](https://github.com/tiangolo). * 👷 Update GitHub Actions to download and upload artifacts. PR [#936](https://github.com/tiangolo/sqlmodel/pull/936) by [@tiangolo](https://github.com/tiangolo). * 👷 Tweak CI for test-redistribute, add needed env vars for slim. PR [#929](https://github.com/tiangolo/sqlmodel/pull/929) by [@tiangolo](https://github.com/tiangolo). From bd1641c9a21cadd5f1df3401541675b230f4a87d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 3 Jun 2024 20:39:07 -0500 Subject: [PATCH 26/46] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Update=20minimum=20S?= =?UTF-8?q?QLAlchemy=20version=20to=202.0.14=20as=20that=20one=20includes?= =?UTF-8?q?=20`TryCast`=20used=20internally=20(#964)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ade520ab48..3d6f1c291d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ classifiers = [ ] dependencies = [ - "SQLAlchemy >=2.0.0,<2.1.0", + "SQLAlchemy >=2.0.14,<2.1.0", "pydantic >=1.10.13,<3.0.0", ] From d5cba6e35842dcfcbbb1273ce9dc50a87252bb58 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 4 Jun 2024 01:39:25 +0000 Subject: [PATCH 27/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index de348cf2d7..9d9d6f5fff 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -6,6 +6,10 @@ * 🐛 Fix set varchar limit when `max_length` is set on Pydantic models using Pydantic v2. PR [#963](https://github.com/tiangolo/sqlmodel/pull/963) by [@estebanx64](https://github.com/estebanx64). +### Upgrades + +* ⬆️ Update minimum SQLAlchemy version to 2.0.14 as that one includes `TryCast` used internally. PR [#964](https://github.com/tiangolo/sqlmodel/pull/964) by [@tiangolo](https://github.com/tiangolo). + ### Docs * ✏️ Fix broken link to `@dataclass_transform` (now PEP 681) in `docs/features.md`. PR [#753](https://github.com/tiangolo/sqlmodel/pull/753) by [@soof-golan](https://github.com/soof-golan). From d165e4b5ad20ea9ecc5f4791b5a78ffd2b3aaeda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 3 Jun 2024 21:34:54 -0500 Subject: [PATCH 28/46] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20generate?= =?UTF-8?q?=20select=20template=20to=20isolate=20templated=20code=20to=20t?= =?UTF-8?q?he=20minimum=20(#967)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- scripts/generate_select.py | 8 +- sqlmodel/sql/_expression_select_cls.py | 43 ++ sqlmodel/sql/_expression_select_gen.py | 396 ++++++++++++++++ sqlmodel/sql/_expression_select_gen.py.jinja2 | 86 ++++ sqlmodel/sql/expression.py | 430 +----------------- sqlmodel/sql/expression.py.jinja2 | 309 ------------- 7 files changed, 544 insertions(+), 730 deletions(-) create mode 100644 sqlmodel/sql/_expression_select_cls.py create mode 100644 sqlmodel/sql/_expression_select_gen.py create mode 100644 sqlmodel/sql/_expression_select_gen.py.jinja2 delete mode 100644 sqlmodel/sql/expression.py.jinja2 diff --git a/pyproject.toml b/pyproject.toml index 3d6f1c291d..14a1432ce7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,7 +90,7 @@ exclude_lines = [ strict = true [[tool.mypy.overrides]] -module = "sqlmodel.sql.expression" +module = "sqlmodel.sql._expression_select_gen" warn_unused_ignores = false [[tool.mypy.overrides]] diff --git a/scripts/generate_select.py b/scripts/generate_select.py index 8f01cb808d..49c2b2b015 100644 --- a/scripts/generate_select.py +++ b/scripts/generate_select.py @@ -7,8 +7,10 @@ from jinja2 import Template from pydantic import BaseModel -template_path = Path(__file__).parent.parent / "sqlmodel/sql/expression.py.jinja2" -destiny_path = Path(__file__).parent.parent / "sqlmodel/sql/expression.py" +template_path = ( + Path(__file__).parent.parent / "sqlmodel/sql/_expression_select_gen.py.jinja2" +) +destiny_path = Path(__file__).parent.parent / "sqlmodel/sql/_expression_select_gen.py" number_of_types = 4 @@ -48,7 +50,7 @@ class Arg(BaseModel): result = ( "# WARNING: do not modify this code, it is generated by " - "expression.py.jinja2\n\n" + result + "_expression_select_gen.py.jinja2\n\n" + result ) result = black.format_str(result, mode=black.Mode()) diff --git a/sqlmodel/sql/_expression_select_cls.py b/sqlmodel/sql/_expression_select_cls.py new file mode 100644 index 0000000000..9fd8609956 --- /dev/null +++ b/sqlmodel/sql/_expression_select_cls.py @@ -0,0 +1,43 @@ +from typing import ( + Tuple, + TypeVar, + Union, +) + +from sqlalchemy.sql._typing import ( + _ColumnExpressionArgument, +) +from sqlalchemy.sql.expression import Select as _Select +from typing_extensions import Self + +_T = TypeVar("_T") + + +# Separate this class in SelectBase, Select, and SelectOfScalar so that they can share +# where and having without having type overlap incompatibility in session.exec(). +class SelectBase(_Select[Tuple[_T]]): + inherit_cache = True + + def where(self, *whereclause: Union[_ColumnExpressionArgument[bool], bool]) -> Self: + """Return a new `Select` construct with the given expression added to + its `WHERE` clause, joined to the existing clause via `AND`, if any. + """ + return super().where(*whereclause) # type: ignore[arg-type] + + def having(self, *having: Union[_ColumnExpressionArgument[bool], bool]) -> Self: + """Return a new `Select` construct with the given expression added to + its `HAVING` clause, joined to the existing clause via `AND`, if any. + """ + return super().having(*having) # type: ignore[arg-type] + + +class Select(SelectBase[_T]): + 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(SelectBase[_T]): + inherit_cache = True diff --git a/sqlmodel/sql/_expression_select_gen.py b/sqlmodel/sql/_expression_select_gen.py new file mode 100644 index 0000000000..b6c15742fa --- /dev/null +++ b/sqlmodel/sql/_expression_select_gen.py @@ -0,0 +1,396 @@ +# WARNING: do not modify this code, it is generated by _expression_select_gen.py.jinja2 + +from datetime import datetime +from typing import ( + Any, + Mapping, + Sequence, + Tuple, + Type, + TypeVar, + Union, + overload, +) +from uuid import UUID + +from sqlalchemy import ( + Column, +) +from sqlalchemy.sql.elements import ( + SQLCoreOperations, +) +from sqlalchemy.sql.roles import TypedColumnsClauseRole + +from ._expression_select_cls import Select, SelectOfScalar + +_T = TypeVar("_T") + + +_TCCA = Union[ + TypedColumnsClauseRole[_T], + SQLCoreOperations[_T], + Type[_T], +] + +# Generated TypeVars start + + +_TScalar_0 = TypeVar( + "_TScalar_0", + Column, # type: ignore + Sequence, # type: ignore + Mapping, # type: ignore + UUID, + datetime, + float, + int, + bool, + bytes, + str, + None, +) + +_T0 = TypeVar("_T0") + + +_TScalar_1 = TypeVar( + "_TScalar_1", + Column, # type: ignore + Sequence, # type: ignore + Mapping, # type: ignore + UUID, + datetime, + float, + int, + bool, + bytes, + str, + None, +) + +_T1 = TypeVar("_T1") + + +_TScalar_2 = TypeVar( + "_TScalar_2", + Column, # type: ignore + Sequence, # type: ignore + Mapping, # type: ignore + UUID, + datetime, + float, + int, + bool, + bytes, + str, + None, +) + +_T2 = TypeVar("_T2") + + +_TScalar_3 = TypeVar( + "_TScalar_3", + Column, # type: ignore + Sequence, # type: ignore + Mapping, # type: ignore + UUID, + datetime, + float, + int, + bool, + bytes, + str, + None, +) + +_T3 = TypeVar("_T3") + + +# Generated TypeVars end + + +@overload +def select(__ent0: _TCCA[_T0]) -> SelectOfScalar[_T0]: + ... + + +@overload +def select(__ent0: _TScalar_0) -> SelectOfScalar[_TScalar_0]: # type: ignore + ... + + +# Generated overloads start + + +@overload +def select( # type: ignore + __ent0: _TCCA[_T0], + __ent1: _TCCA[_T1], +) -> Select[Tuple[_T0, _T1]]: + ... + + +@overload +def select( # type: ignore + __ent0: _TCCA[_T0], + entity_1: _TScalar_1, +) -> Select[Tuple[_T0, _TScalar_1]]: + ... + + +@overload +def select( # type: ignore + entity_0: _TScalar_0, + __ent1: _TCCA[_T1], +) -> Select[Tuple[_TScalar_0, _T1]]: + ... + + +@overload +def select( # type: ignore + entity_0: _TScalar_0, + entity_1: _TScalar_1, +) -> Select[Tuple[_TScalar_0, _TScalar_1]]: + ... + + +@overload +def select( # type: ignore + __ent0: _TCCA[_T0], + __ent1: _TCCA[_T1], + __ent2: _TCCA[_T2], +) -> Select[Tuple[_T0, _T1, _T2]]: + ... + + +@overload +def select( # type: ignore + __ent0: _TCCA[_T0], + __ent1: _TCCA[_T1], + entity_2: _TScalar_2, +) -> Select[Tuple[_T0, _T1, _TScalar_2]]: + ... + + +@overload +def select( # type: ignore + __ent0: _TCCA[_T0], + entity_1: _TScalar_1, + __ent2: _TCCA[_T2], +) -> Select[Tuple[_T0, _TScalar_1, _T2]]: + ... + + +@overload +def select( # type: ignore + __ent0: _TCCA[_T0], + entity_1: _TScalar_1, + entity_2: _TScalar_2, +) -> Select[Tuple[_T0, _TScalar_1, _TScalar_2]]: + ... + + +@overload +def select( # type: ignore + entity_0: _TScalar_0, + __ent1: _TCCA[_T1], + __ent2: _TCCA[_T2], +) -> Select[Tuple[_TScalar_0, _T1, _T2]]: + ... + + +@overload +def select( # type: ignore + entity_0: _TScalar_0, + __ent1: _TCCA[_T1], + entity_2: _TScalar_2, +) -> Select[Tuple[_TScalar_0, _T1, _TScalar_2]]: + ... + + +@overload +def select( # type: ignore + entity_0: _TScalar_0, + entity_1: _TScalar_1, + __ent2: _TCCA[_T2], +) -> Select[Tuple[_TScalar_0, _TScalar_1, _T2]]: + ... + + +@overload +def select( # type: ignore + entity_0: _TScalar_0, + entity_1: _TScalar_1, + entity_2: _TScalar_2, +) -> Select[Tuple[_TScalar_0, _TScalar_1, _TScalar_2]]: + ... + + +@overload +def select( # type: ignore + __ent0: _TCCA[_T0], + __ent1: _TCCA[_T1], + __ent2: _TCCA[_T2], + __ent3: _TCCA[_T3], +) -> Select[Tuple[_T0, _T1, _T2, _T3]]: + ... + + +@overload +def select( # type: ignore + __ent0: _TCCA[_T0], + __ent1: _TCCA[_T1], + __ent2: _TCCA[_T2], + entity_3: _TScalar_3, +) -> Select[Tuple[_T0, _T1, _T2, _TScalar_3]]: + ... + + +@overload +def select( # type: ignore + __ent0: _TCCA[_T0], + __ent1: _TCCA[_T1], + entity_2: _TScalar_2, + __ent3: _TCCA[_T3], +) -> Select[Tuple[_T0, _T1, _TScalar_2, _T3]]: + ... + + +@overload +def select( # type: ignore + __ent0: _TCCA[_T0], + __ent1: _TCCA[_T1], + entity_2: _TScalar_2, + entity_3: _TScalar_3, +) -> Select[Tuple[_T0, _T1, _TScalar_2, _TScalar_3]]: + ... + + +@overload +def select( # type: ignore + __ent0: _TCCA[_T0], + entity_1: _TScalar_1, + __ent2: _TCCA[_T2], + __ent3: _TCCA[_T3], +) -> Select[Tuple[_T0, _TScalar_1, _T2, _T3]]: + ... + + +@overload +def select( # type: ignore + __ent0: _TCCA[_T0], + entity_1: _TScalar_1, + __ent2: _TCCA[_T2], + entity_3: _TScalar_3, +) -> Select[Tuple[_T0, _TScalar_1, _T2, _TScalar_3]]: + ... + + +@overload +def select( # type: ignore + __ent0: _TCCA[_T0], + entity_1: _TScalar_1, + entity_2: _TScalar_2, + __ent3: _TCCA[_T3], +) -> Select[Tuple[_T0, _TScalar_1, _TScalar_2, _T3]]: + ... + + +@overload +def select( # type: ignore + __ent0: _TCCA[_T0], + entity_1: _TScalar_1, + entity_2: _TScalar_2, + entity_3: _TScalar_3, +) -> Select[Tuple[_T0, _TScalar_1, _TScalar_2, _TScalar_3]]: + ... + + +@overload +def select( # type: ignore + entity_0: _TScalar_0, + __ent1: _TCCA[_T1], + __ent2: _TCCA[_T2], + __ent3: _TCCA[_T3], +) -> Select[Tuple[_TScalar_0, _T1, _T2, _T3]]: + ... + + +@overload +def select( # type: ignore + entity_0: _TScalar_0, + __ent1: _TCCA[_T1], + __ent2: _TCCA[_T2], + entity_3: _TScalar_3, +) -> Select[Tuple[_TScalar_0, _T1, _T2, _TScalar_3]]: + ... + + +@overload +def select( # type: ignore + entity_0: _TScalar_0, + __ent1: _TCCA[_T1], + entity_2: _TScalar_2, + __ent3: _TCCA[_T3], +) -> Select[Tuple[_TScalar_0, _T1, _TScalar_2, _T3]]: + ... + + +@overload +def select( # type: ignore + entity_0: _TScalar_0, + __ent1: _TCCA[_T1], + entity_2: _TScalar_2, + entity_3: _TScalar_3, +) -> Select[Tuple[_TScalar_0, _T1, _TScalar_2, _TScalar_3]]: + ... + + +@overload +def select( # type: ignore + entity_0: _TScalar_0, + entity_1: _TScalar_1, + __ent2: _TCCA[_T2], + __ent3: _TCCA[_T3], +) -> Select[Tuple[_TScalar_0, _TScalar_1, _T2, _T3]]: + ... + + +@overload +def select( # type: ignore + entity_0: _TScalar_0, + entity_1: _TScalar_1, + __ent2: _TCCA[_T2], + entity_3: _TScalar_3, +) -> Select[Tuple[_TScalar_0, _TScalar_1, _T2, _TScalar_3]]: + ... + + +@overload +def select( # type: ignore + entity_0: _TScalar_0, + entity_1: _TScalar_1, + entity_2: _TScalar_2, + __ent3: _TCCA[_T3], +) -> Select[Tuple[_TScalar_0, _TScalar_1, _TScalar_2, _T3]]: + ... + + +@overload +def select( # type: ignore + entity_0: _TScalar_0, + entity_1: _TScalar_1, + entity_2: _TScalar_2, + entity_3: _TScalar_3, +) -> Select[Tuple[_TScalar_0, _TScalar_1, _TScalar_2, _TScalar_3]]: + ... + + +# Generated overloads end + + +def select(*entities: Any) -> Union[Select, SelectOfScalar]: # type: ignore + if len(entities) == 1: + return SelectOfScalar(*entities) + return Select(*entities) diff --git a/sqlmodel/sql/_expression_select_gen.py.jinja2 b/sqlmodel/sql/_expression_select_gen.py.jinja2 new file mode 100644 index 0000000000..307e32b784 --- /dev/null +++ b/sqlmodel/sql/_expression_select_gen.py.jinja2 @@ -0,0 +1,86 @@ +from datetime import datetime +from typing import ( + Any, + Mapping, + Sequence, + Tuple, + Type, + TypeVar, + Union, + overload, +) +from uuid import UUID + +from sqlalchemy import ( + Column, +) +from sqlalchemy.sql.elements import ( + SQLCoreOperations, +) +from sqlalchemy.sql.roles import TypedColumnsClauseRole + +from ._expression_select_cls import Select, SelectOfScalar + +_T = TypeVar("_T") + + +_TCCA = Union[ + TypedColumnsClauseRole[_T], + SQLCoreOperations[_T], + Type[_T], +] + +# Generated TypeVars start + + +{% for i in range(number_of_types) %} +_TScalar_{{ i }} = TypeVar( + "_TScalar_{{ i }}", + Column, # type: ignore + Sequence, # type: ignore + Mapping, # type: ignore + UUID, + datetime, + float, + int, + bool, + bytes, + str, + None, +) + +_T{{ i }} = TypeVar("_T{{ i }}") + +{% endfor %} + +# Generated TypeVars end + +@overload +def select(__ent0: _TCCA[_T0]) -> SelectOfScalar[_T0]: + ... + + +@overload +def select(__ent0: _TScalar_0) -> SelectOfScalar[_TScalar_0]: # type: ignore + ... + + +# Generated overloads start + +{% for signature in signatures %} + +@overload +def select( # type: ignore + {% for arg in signature[0] %}{{ arg.name }}: {{ arg.annotation }}, {% endfor %} + ) -> Select[Tuple[{%for ret in signature[1] %}{{ ret }} {% if not loop.last %}, {% endif %}{% endfor %}]]: + ... + +{% endfor %} + +# Generated overloads end + + +def select(*entities: Any) -> Union[Select, SelectOfScalar]: # type: ignore + if len(entities) == 1: + return SelectOfScalar(*entities) + return Select(*entities) diff --git a/sqlmodel/sql/expression.py b/sqlmodel/sql/expression.py index 112968c655..f431747670 100644 --- a/sqlmodel/sql/expression.py +++ b/sqlmodel/sql/expression.py @@ -1,6 +1,3 @@ -# WARNING: do not modify this code, it is generated by expression.py.jinja2 - -from datetime import datetime from typing import ( Any, Iterable, @@ -11,9 +8,7 @@ Type, TypeVar, Union, - overload, ) -from uuid import UUID import sqlalchemy from sqlalchemy import ( @@ -39,14 +34,15 @@ Cast, CollectionAggregate, ColumnClause, - SQLCoreOperations, TryCast, UnaryExpression, ) -from sqlalchemy.sql.expression import Select as _Select -from sqlalchemy.sql.roles import TypedColumnsClauseRole from sqlalchemy.sql.type_api import TypeEngine -from typing_extensions import Literal, Self +from typing_extensions import Literal + +from ._expression_select_cls import Select as Select +from ._expression_select_cls import SelectOfScalar as SelectOfScalar +from ._expression_select_gen import select as select _T = TypeVar("_T") @@ -89,7 +85,7 @@ def between( upper_bound: Any, symmetric: bool = False, ) -> BinaryExpression[bool]: - return sqlalchemy.between(expr, lower_bound, upper_bound, symmetric=symmetric) # type: ignore[arg-type] + return sqlalchemy.between(expr, lower_bound, upper_bound, symmetric=symmetric) def not_(clause: Union[_ColumnExpressionArgument[_T], _T]) -> ColumnElement[_T]: @@ -110,14 +106,14 @@ def cast( expression: Union[_ColumnExpressionOrLiteralArgument[Any], Any], type_: "_TypeEngineArgument[_T]", ) -> Cast[_T]: - return sqlalchemy.cast(expression, type_) # type: ignore[arg-type] + return sqlalchemy.cast(expression, type_) def try_cast( expression: Union[_ColumnExpressionOrLiteralArgument[Any], Any], type_: "_TypeEngineArgument[_T]", ) -> TryCast[_T]: - return sqlalchemy.try_cast(expression, type_) # type: ignore[arg-type] + return sqlalchemy.try_cast(expression, type_) def desc( @@ -135,7 +131,7 @@ def bitwise_not(expr: Union[_ColumnExpressionArgument[_T], _T]) -> UnaryExpressi def extract(field: str, expr: Union[_ColumnExpressionArgument[Any], Any]) -> Extract: - return sqlalchemy.extract(field, expr) # type: ignore[arg-type] + return sqlalchemy.extract(field, expr) def funcfilter( @@ -162,7 +158,7 @@ def nulls_last(column: Union[_ColumnExpressionArgument[_T], _T]) -> UnaryExpress return sqlalchemy.nulls_last(column) # type: ignore[arg-type] -def or_( # type: ignore[empty-body] +def or_( initial_clause: Union[Literal[False], _ColumnExpressionArgument[bool], bool], *clauses: Union[_ColumnExpressionArgument[bool], bool], ) -> ColumnElement[bool]: @@ -190,7 +186,7 @@ def over( ) -> Over[_T]: return sqlalchemy.over( element, partition_by=partition_by, order_by=order_by, range_=range_, rows=rows - ) # type: ignore[arg-type] + ) def tuple_( @@ -204,413 +200,13 @@ def type_coerce( expression: Union[_ColumnExpressionOrLiteralArgument[Any], Any], type_: "_TypeEngineArgument[_T]", ) -> TypeCoerce[_T]: - return sqlalchemy.type_coerce(expression, type_) # type: ignore[arg-type] + return sqlalchemy.type_coerce(expression, type_) def within_group( element: FunctionElement[_T], *order_by: Union[_ColumnExpressionArgument[Any], Any] ) -> WithinGroup[_T]: - return sqlalchemy.within_group(element, *order_by) # type: ignore[arg-type] - - -# Separate this class in SelectBase, Select, and SelectOfScalar so that they can share -# where and having without having type overlap incompatibility in session.exec(). -class SelectBase(_Select[Tuple[_T]]): - inherit_cache = True - - def where(self, *whereclause: Union[_ColumnExpressionArgument[bool], bool]) -> Self: - """Return a new `Select` construct with the given expression added to - its `WHERE` clause, joined to the existing clause via `AND`, if any. - """ - return super().where(*whereclause) # type: ignore[arg-type] - - def having(self, *having: Union[_ColumnExpressionArgument[bool], bool]) -> Self: - """Return a new `Select` construct with the given expression added to - its `HAVING` clause, joined to the existing clause via `AND`, if any. - """ - return super().having(*having) # type: ignore[arg-type] - - -class Select(SelectBase[_T]): - 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(SelectBase[_T]): - inherit_cache = True - - -_TCCA = Union[ - TypedColumnsClauseRole[_T], - SQLCoreOperations[_T], - Type[_T], -] - -# Generated TypeVars start - - -_TScalar_0 = TypeVar( - "_TScalar_0", - Column, # type: ignore - Sequence, # type: ignore - Mapping, # type: ignore - UUID, - datetime, - float, - int, - bool, - bytes, - str, - None, -) - -_T0 = TypeVar("_T0") - - -_TScalar_1 = TypeVar( - "_TScalar_1", - Column, # type: ignore - Sequence, # type: ignore - Mapping, # type: ignore - UUID, - datetime, - float, - int, - bool, - bytes, - str, - None, -) - -_T1 = TypeVar("_T1") - - -_TScalar_2 = TypeVar( - "_TScalar_2", - Column, # type: ignore - Sequence, # type: ignore - Mapping, # type: ignore - UUID, - datetime, - float, - int, - bool, - bytes, - str, - None, -) - -_T2 = TypeVar("_T2") - - -_TScalar_3 = TypeVar( - "_TScalar_3", - Column, # type: ignore - Sequence, # type: ignore - Mapping, # type: ignore - UUID, - datetime, - float, - int, - bool, - bytes, - str, - None, -) - -_T3 = TypeVar("_T3") - - -# Generated TypeVars end - - -@overload -def select(__ent0: _TCCA[_T0]) -> SelectOfScalar[_T0]: - ... - - -@overload -def select(__ent0: _TScalar_0) -> SelectOfScalar[_TScalar_0]: # type: ignore - ... - - -# Generated overloads start - - -@overload -def select( # type: ignore - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], -) -> Select[Tuple[_T0, _T1]]: - ... - - -@overload -def select( # type: ignore - __ent0: _TCCA[_T0], - entity_1: _TScalar_1, -) -> Select[Tuple[_T0, _TScalar_1]]: - ... - - -@overload -def select( # type: ignore - entity_0: _TScalar_0, - __ent1: _TCCA[_T1], -) -> Select[Tuple[_TScalar_0, _T1]]: - ... - - -@overload -def select( # type: ignore - entity_0: _TScalar_0, - entity_1: _TScalar_1, -) -> Select[Tuple[_TScalar_0, _TScalar_1]]: - ... - - -@overload -def select( # type: ignore - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], -) -> Select[Tuple[_T0, _T1, _T2]]: - ... - - -@overload -def select( # type: ignore - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - entity_2: _TScalar_2, -) -> Select[Tuple[_T0, _T1, _TScalar_2]]: - ... - - -@overload -def select( # type: ignore - __ent0: _TCCA[_T0], - entity_1: _TScalar_1, - __ent2: _TCCA[_T2], -) -> Select[Tuple[_T0, _TScalar_1, _T2]]: - ... - - -@overload -def select( # type: ignore - __ent0: _TCCA[_T0], - entity_1: _TScalar_1, - entity_2: _TScalar_2, -) -> Select[Tuple[_T0, _TScalar_1, _TScalar_2]]: - ... - - -@overload -def select( # type: ignore - entity_0: _TScalar_0, - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], -) -> Select[Tuple[_TScalar_0, _T1, _T2]]: - ... - - -@overload -def select( # type: ignore - entity_0: _TScalar_0, - __ent1: _TCCA[_T1], - entity_2: _TScalar_2, -) -> Select[Tuple[_TScalar_0, _T1, _TScalar_2]]: - ... - - -@overload -def select( # type: ignore - entity_0: _TScalar_0, - entity_1: _TScalar_1, - __ent2: _TCCA[_T2], -) -> Select[Tuple[_TScalar_0, _TScalar_1, _T2]]: - ... - - -@overload -def select( # type: ignore - entity_0: _TScalar_0, - entity_1: _TScalar_1, - entity_2: _TScalar_2, -) -> Select[Tuple[_TScalar_0, _TScalar_1, _TScalar_2]]: - ... - - -@overload -def select( # type: ignore - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], -) -> Select[Tuple[_T0, _T1, _T2, _T3]]: - ... - - -@overload -def select( # type: ignore - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - entity_3: _TScalar_3, -) -> Select[Tuple[_T0, _T1, _T2, _TScalar_3]]: - ... - - -@overload -def select( # type: ignore - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - entity_2: _TScalar_2, - __ent3: _TCCA[_T3], -) -> Select[Tuple[_T0, _T1, _TScalar_2, _T3]]: - ... - - -@overload -def select( # type: ignore - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - entity_2: _TScalar_2, - entity_3: _TScalar_3, -) -> Select[Tuple[_T0, _T1, _TScalar_2, _TScalar_3]]: - ... - - -@overload -def select( # type: ignore - __ent0: _TCCA[_T0], - entity_1: _TScalar_1, - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], -) -> Select[Tuple[_T0, _TScalar_1, _T2, _T3]]: - ... - - -@overload -def select( # type: ignore - __ent0: _TCCA[_T0], - entity_1: _TScalar_1, - __ent2: _TCCA[_T2], - entity_3: _TScalar_3, -) -> Select[Tuple[_T0, _TScalar_1, _T2, _TScalar_3]]: - ... - - -@overload -def select( # type: ignore - __ent0: _TCCA[_T0], - entity_1: _TScalar_1, - entity_2: _TScalar_2, - __ent3: _TCCA[_T3], -) -> Select[Tuple[_T0, _TScalar_1, _TScalar_2, _T3]]: - ... - - -@overload -def select( # type: ignore - __ent0: _TCCA[_T0], - entity_1: _TScalar_1, - entity_2: _TScalar_2, - entity_3: _TScalar_3, -) -> Select[Tuple[_T0, _TScalar_1, _TScalar_2, _TScalar_3]]: - ... - - -@overload -def select( # type: ignore - entity_0: _TScalar_0, - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], -) -> Select[Tuple[_TScalar_0, _T1, _T2, _T3]]: - ... - - -@overload -def select( # type: ignore - entity_0: _TScalar_0, - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - entity_3: _TScalar_3, -) -> Select[Tuple[_TScalar_0, _T1, _T2, _TScalar_3]]: - ... - - -@overload -def select( # type: ignore - entity_0: _TScalar_0, - __ent1: _TCCA[_T1], - entity_2: _TScalar_2, - __ent3: _TCCA[_T3], -) -> Select[Tuple[_TScalar_0, _T1, _TScalar_2, _T3]]: - ... - - -@overload -def select( # type: ignore - entity_0: _TScalar_0, - __ent1: _TCCA[_T1], - entity_2: _TScalar_2, - entity_3: _TScalar_3, -) -> Select[Tuple[_TScalar_0, _T1, _TScalar_2, _TScalar_3]]: - ... - - -@overload -def select( # type: ignore - entity_0: _TScalar_0, - entity_1: _TScalar_1, - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], -) -> Select[Tuple[_TScalar_0, _TScalar_1, _T2, _T3]]: - ... - - -@overload -def select( # type: ignore - entity_0: _TScalar_0, - entity_1: _TScalar_1, - __ent2: _TCCA[_T2], - entity_3: _TScalar_3, -) -> Select[Tuple[_TScalar_0, _TScalar_1, _T2, _TScalar_3]]: - ... - - -@overload -def select( # type: ignore - entity_0: _TScalar_0, - entity_1: _TScalar_1, - entity_2: _TScalar_2, - __ent3: _TCCA[_T3], -) -> Select[Tuple[_TScalar_0, _TScalar_1, _TScalar_2, _T3]]: - ... - - -@overload -def select( # type: ignore - entity_0: _TScalar_0, - entity_1: _TScalar_1, - entity_2: _TScalar_2, - entity_3: _TScalar_3, -) -> Select[Tuple[_TScalar_0, _TScalar_1, _TScalar_2, _TScalar_3]]: - ... - - -# Generated overloads end - - -def select(*entities: Any) -> Union[Select, SelectOfScalar]: # type: ignore - if len(entities) == 1: - return SelectOfScalar(*entities) - return Select(*entities) + return sqlalchemy.within_group(element, *order_by) def col(column_expression: _T) -> Mapped[_T]: diff --git a/sqlmodel/sql/expression.py.jinja2 b/sqlmodel/sql/expression.py.jinja2 deleted file mode 100644 index 53babe1bbe..0000000000 --- a/sqlmodel/sql/expression.py.jinja2 +++ /dev/null @@ -1,309 +0,0 @@ -from datetime import datetime -from typing import ( - Any, - Iterable, - Mapping, - Optional, - Sequence, - Tuple, - Type, - TypeVar, - Union, - overload, -) -from uuid import UUID - -import sqlalchemy -from sqlalchemy import ( - Column, - ColumnElement, - Extract, - FunctionElement, - FunctionFilter, - Label, - Over, - TypeCoerce, - WithinGroup, -) -from sqlalchemy.orm import InstrumentedAttribute, Mapped -from sqlalchemy.sql._typing import ( - _ColumnExpressionArgument, - _ColumnExpressionOrLiteralArgument, - _ColumnExpressionOrStrLabelArgument, -) -from sqlalchemy.sql.elements import ( - BinaryExpression, - Case, - Cast, - CollectionAggregate, - ColumnClause, - SQLCoreOperations, - TryCast, - UnaryExpression, -) -from sqlalchemy.sql.expression import Select as _Select -from sqlalchemy.sql.roles import TypedColumnsClauseRole -from sqlalchemy.sql.type_api import TypeEngine -from typing_extensions import Literal, Self - -_T = TypeVar("_T") - -_TypeEngineArgument = Union[Type[TypeEngine[_T]], TypeEngine[_T]] - -# Redefine operatos that would only take a column expresion to also take the (virtual) -# types of Pydantic models, e.g. str instead of only Mapped[str]. - - -def all_(expr: Union[_ColumnExpressionArgument[_T], _T]) -> CollectionAggregate[bool]: - return sqlalchemy.all_(expr) # type: ignore[arg-type] - - -def and_( - initial_clause: Union[Literal[True], _ColumnExpressionArgument[bool], bool], - *clauses: Union[_ColumnExpressionArgument[bool], bool], -) -> ColumnElement[bool]: - return sqlalchemy.and_(initial_clause, *clauses) # type: ignore[arg-type] - - -def any_(expr: Union[_ColumnExpressionArgument[_T], _T]) -> CollectionAggregate[bool]: - return sqlalchemy.any_(expr) # type: ignore[arg-type] - - -def asc( - column: Union[_ColumnExpressionOrStrLabelArgument[_T], _T], -) -> UnaryExpression[_T]: - return sqlalchemy.asc(column) # type: ignore[arg-type] - - -def collate( - expression: Union[_ColumnExpressionArgument[str], str], collation: str -) -> BinaryExpression[str]: - return sqlalchemy.collate(expression, collation) # type: ignore[arg-type] - - -def between( - expr: Union[_ColumnExpressionOrLiteralArgument[_T], _T], - lower_bound: Any, - upper_bound: Any, - symmetric: bool = False, -) -> BinaryExpression[bool]: - return sqlalchemy.between(expr, lower_bound, upper_bound, symmetric=symmetric) # type: ignore[arg-type] - - -def not_(clause: Union[_ColumnExpressionArgument[_T], _T]) -> ColumnElement[_T]: - return sqlalchemy.not_(clause) # type: ignore[arg-type] - - -def case( - *whens: Union[ - Tuple[Union[_ColumnExpressionArgument[bool], bool], Any], Mapping[Any, Any] - ], - value: Optional[Any] = None, - else_: Optional[Any] = None, -) -> Case[Any]: - return sqlalchemy.case(*whens, value=value, else_=else_) # type: ignore[arg-type] - - -def cast( - expression: Union[_ColumnExpressionOrLiteralArgument[Any], Any], - type_: "_TypeEngineArgument[_T]", -) -> Cast[_T]: - return sqlalchemy.cast(expression, type_) # type: ignore[arg-type] - - -def try_cast( - expression: Union[_ColumnExpressionOrLiteralArgument[Any], Any], - type_: "_TypeEngineArgument[_T]", -) -> TryCast[_T]: - return sqlalchemy.try_cast(expression, type_) # type: ignore[arg-type] - - -def desc( - column: Union[_ColumnExpressionOrStrLabelArgument[_T], _T], -) -> UnaryExpression[_T]: - return sqlalchemy.desc(column) # type: ignore[arg-type] - - -def distinct(expr: Union[_ColumnExpressionArgument[_T], _T]) -> UnaryExpression[_T]: - return sqlalchemy.distinct(expr) # type: ignore[arg-type] - - -def bitwise_not(expr: Union[_ColumnExpressionArgument[_T], _T]) -> UnaryExpression[_T]: - return sqlalchemy.bitwise_not(expr) # type: ignore[arg-type] - - -def extract(field: str, expr: Union[_ColumnExpressionArgument[Any], Any]) -> Extract: - return sqlalchemy.extract(field, expr) # type: ignore[arg-type] - - -def funcfilter( - func: FunctionElement[_T], *criterion: Union[_ColumnExpressionArgument[bool], bool] -) -> FunctionFilter[_T]: - return sqlalchemy.funcfilter(func, *criterion) # type: ignore[arg-type] - - -def label( - name: str, - element: Union[_ColumnExpressionArgument[_T], _T], - type_: Optional["_TypeEngineArgument[_T]"] = None, -) -> Label[_T]: - return sqlalchemy.label(name, element, type_=type_) # type: ignore[arg-type] - - -def nulls_first( - column: Union[_ColumnExpressionArgument[_T], _T], -) -> UnaryExpression[_T]: - return sqlalchemy.nulls_first(column) # type: ignore[arg-type] - - -def nulls_last(column: Union[_ColumnExpressionArgument[_T], _T]) -> UnaryExpression[_T]: - return sqlalchemy.nulls_last(column) # type: ignore[arg-type] - - -def or_( # type: ignore[empty-body] - initial_clause: Union[Literal[False], _ColumnExpressionArgument[bool], bool], - *clauses: Union[_ColumnExpressionArgument[bool], bool], -) -> ColumnElement[bool]: - return sqlalchemy.or_(initial_clause, *clauses) # type: ignore[arg-type] - - -def over( - element: FunctionElement[_T], - partition_by: Optional[ - Union[ - Iterable[Union[_ColumnExpressionArgument[Any], Any]], - _ColumnExpressionArgument[Any], - Any, - ] - ] = None, - order_by: Optional[ - Union[ - Iterable[Union[_ColumnExpressionArgument[Any], Any]], - _ColumnExpressionArgument[Any], - Any, - ] - ] = None, - range_: Optional[Tuple[Optional[int], Optional[int]]] = None, - rows: Optional[Tuple[Optional[int], Optional[int]]] = None, -) -> Over[_T]: - return sqlalchemy.over( - element, partition_by=partition_by, order_by=order_by, range_=range_, rows=rows - ) # type: ignore[arg-type] - - -def tuple_( - *clauses: Union[_ColumnExpressionArgument[Any], Any], - types: Optional[Sequence["_TypeEngineArgument[Any]"]] = None, -) -> Tuple[Any, ...]: - return sqlalchemy.tuple_(*clauses, types=types) # type: ignore[return-value] - - -def type_coerce( - expression: Union[_ColumnExpressionOrLiteralArgument[Any], Any], - type_: "_TypeEngineArgument[_T]", -) -> TypeCoerce[_T]: - return sqlalchemy.type_coerce(expression, type_) # type: ignore[arg-type] - - -def within_group( - element: FunctionElement[_T], *order_by: Union[_ColumnExpressionArgument[Any], Any] -) -> WithinGroup[_T]: - return sqlalchemy.within_group(element, *order_by) # type: ignore[arg-type] - - -# Separate this class in SelectBase, Select, and SelectOfScalar so that they can share -# where and having without having type overlap incompatibility in session.exec(). -class SelectBase(_Select[Tuple[_T]]): - inherit_cache = True - - def where(self, *whereclause: Union[_ColumnExpressionArgument[bool], bool]) -> Self: - """Return a new `Select` construct with the given expression added to - its `WHERE` clause, joined to the existing clause via `AND`, if any. - """ - return super().where(*whereclause) # type: ignore[arg-type] - - def having(self, *having: Union[_ColumnExpressionArgument[bool], bool]) -> Self: - """Return a new `Select` construct with the given expression added to - its `HAVING` clause, joined to the existing clause via `AND`, if any. - """ - return super().having(*having) # type: ignore[arg-type] - - -class Select(SelectBase[_T]): - 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(SelectBase[_T]): - inherit_cache = True - - -_TCCA = Union[ - TypedColumnsClauseRole[_T], - SQLCoreOperations[_T], - Type[_T], -] - -# Generated TypeVars start - - -{% for i in range(number_of_types) %} -_TScalar_{{ i }} = TypeVar( - "_TScalar_{{ i }}", - Column, # type: ignore - Sequence, # type: ignore - Mapping, # type: ignore - UUID, - datetime, - float, - int, - bool, - bytes, - str, - None, -) - -_T{{ i }} = TypeVar("_T{{ i }}") - -{% endfor %} - -# Generated TypeVars end - -@overload -def select(__ent0: _TCCA[_T0]) -> SelectOfScalar[_T0]: - ... - - -@overload -def select(__ent0: _TScalar_0) -> SelectOfScalar[_TScalar_0]: # type: ignore - ... - - -# Generated overloads start - -{% for signature in signatures %} - -@overload -def select( # type: ignore - {% for arg in signature[0] %}{{ arg.name }}: {{ arg.annotation }}, {% endfor %} - ) -> Select[Tuple[{%for ret in signature[1] %}{{ ret }} {% if not loop.last %}, {% endif %}{% endfor %}]]: - ... - -{% endfor %} - -# Generated overloads end - - -def select(*entities: Any) -> Union[Select, SelectOfScalar]: # type: ignore - if len(entities) == 1: - return SelectOfScalar(*entities) - return Select(*entities) - - -def col(column_expression: _T) -> Mapped[_T]: - if not isinstance(column_expression, (ColumnClause, Column, InstrumentedAttribute)): - raise RuntimeError(f"Not a SQLAlchemy column: {column_expression}") - return column_expression # type: ignore From 9f3af8507ebeb045dbfb63656c0a277f4d65761a Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 4 Jun 2024 02:35:16 +0000 Subject: [PATCH 29/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 9d9d6f5fff..2d35084ba3 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -6,6 +6,10 @@ * 🐛 Fix set varchar limit when `max_length` is set on Pydantic models using Pydantic v2. PR [#963](https://github.com/tiangolo/sqlmodel/pull/963) by [@estebanx64](https://github.com/estebanx64). +### Refactors + +* ♻️ Refactor generate select template to isolate templated code to the minimum. PR [#967](https://github.com/tiangolo/sqlmodel/pull/967) by [@tiangolo](https://github.com/tiangolo). + ### Upgrades * ⬆️ Update minimum SQLAlchemy version to 2.0.14 as that one includes `TryCast` used internally. PR [#964](https://github.com/tiangolo/sqlmodel/pull/964) by [@tiangolo](https://github.com/tiangolo). From 1d43bd8b1e26b14b541f77feda0f606916df9699 Mon Sep 17 00:00:00 2001 From: Esteban Maya Date: Mon, 3 Jun 2024 21:47:40 -0500 Subject: [PATCH 30/46] =?UTF-8?q?=F0=9F=90=9B=20Fix=20pydantic=20`EmailStr?= =?UTF-8?q?`=20support=20and=20`max=5Flength`=20in=20several=20String=20su?= =?UTF-8?q?bclasses=20(#966)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sqlmodel/main.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index a16428b192..40051a522c 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -25,7 +25,7 @@ overload, ) -from pydantic import BaseModel +from pydantic import BaseModel, EmailStr from pydantic.fields import FieldInfo as PydanticFieldInfo from sqlalchemy import ( Boolean, @@ -574,7 +574,18 @@ def get_sqlalchemy_type(field: Any) -> Any: # Check enums first as an enum can also be a str, needed by Pydantic/FastAPI if issubclass(type_, Enum): return sa_Enum(type_) - if issubclass(type_, str): + if issubclass( + type_, + ( + str, + ipaddress.IPv4Address, + ipaddress.IPv4Network, + ipaddress.IPv6Address, + ipaddress.IPv6Network, + Path, + EmailStr, + ), + ): max_length = getattr(metadata, "max_length", None) if max_length: return AutoString(length=max_length) @@ -600,16 +611,6 @@ def get_sqlalchemy_type(field: Any) -> Any: precision=getattr(metadata, "max_digits", None), scale=getattr(metadata, "decimal_places", None), ) - if issubclass(type_, ipaddress.IPv4Address): - return AutoString - if issubclass(type_, ipaddress.IPv4Network): - return AutoString - if issubclass(type_, ipaddress.IPv6Address): - return AutoString - if issubclass(type_, ipaddress.IPv6Network): - return AutoString - if issubclass(type_, Path): - return AutoString if issubclass(type_, uuid.UUID): return GUID raise ValueError(f"{type_} has no matching SQLAlchemy type") From ceac7bc2e81f4751b1b4d1889e2be6057e25ea8f Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 4 Jun 2024 02:48:00 +0000 Subject: [PATCH 31/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 2d35084ba3..8dc4d9e62c 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -4,6 +4,7 @@ ### Fixes +* 🐛 Fix pydantic `EmailStr` support and `max_length` in several String subclasses. PR [#966](https://github.com/tiangolo/sqlmodel/pull/966) by [@estebanx64](https://github.com/estebanx64). * 🐛 Fix set varchar limit when `max_length` is set on Pydantic models using Pydantic v2. PR [#963](https://github.com/tiangolo/sqlmodel/pull/963) by [@estebanx64](https://github.com/estebanx64). ### Refactors From b93dd9512543366c078b131970aef1b3d68a9db5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 22:18:48 -0500 Subject: [PATCH 32/46] =?UTF-8?q?=E2=AC=86=20Bump=20tiangolo/issue-manager?= =?UTF-8?q?=20from=200.4.1=20to=200.5.0=20(#922)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [tiangolo/issue-manager](https://github.com/tiangolo/issue-manager) from 0.4.1 to 0.5.0. - [Release notes](https://github.com/tiangolo/issue-manager/releases) - [Commits](https://github.com/tiangolo/issue-manager/compare/0.4.1...0.5.0) --- updated-dependencies: - dependency-name: tiangolo/issue-manager dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/issue-manager.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index 4eeda34b45..68f1391f25 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -18,7 +18,7 @@ jobs: issue-manager: runs-on: ubuntu-latest steps: - - uses: tiangolo/issue-manager@0.4.1 + - uses: tiangolo/issue-manager@0.5.0 with: token: ${{ secrets.GITHUB_TOKEN }} config: > From e2f646dea506233e9f88e122e19bbd7feeb044ed Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 4 Jun 2024 03:19:04 +0000 Subject: [PATCH 33/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 8dc4d9e62c..1acd807efe 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -21,6 +21,7 @@ ### Internal +* ⬆ Bump tiangolo/issue-manager from 0.4.1 to 0.5.0. PR [#922](https://github.com/tiangolo/sqlmodel/pull/922) by [@dependabot[bot]](https://github.com/apps/dependabot). * 📌 Pin typing-extensions in tests for compatiblity with Python 3.8, dirty-equals, Pydantic. PR [#965](https://github.com/tiangolo/sqlmodel/pull/965) by [@tiangolo](https://github.com/tiangolo). * 👷 Update GitHub Actions to download and upload artifacts. PR [#936](https://github.com/tiangolo/sqlmodel/pull/936) by [@tiangolo](https://github.com/tiangolo). * 👷 Tweak CI for test-redistribute, add needed env vars for slim. PR [#929](https://github.com/tiangolo/sqlmodel/pull/929) by [@tiangolo](https://github.com/tiangolo). From b560e9deb8d5ecb9982802e479faf3bedcc7a4b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 3 Jun 2024 22:22:04 -0500 Subject: [PATCH 34/46] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrade=20Ruff=20and?= =?UTF-8?q?=20Black=20(#968)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- requirements-docs-tests.txt | 2 +- requirements-docs.txt | 4 +- requirements-tests.txt | 2 +- scripts/test.sh | 1 - sqlmodel/ext/asyncio/session.py | 6 +- sqlmodel/main.py | 12 +-- sqlmodel/orm/session.py | 6 +- sqlmodel/sql/_expression_select_gen.py | 87 +++++++------------ sqlmodel/sql/_expression_select_gen.py.jinja2 | 6 +- tests/test_select_gen.py | 19 ++++ 11 files changed, 63 insertions(+), 84 deletions(-) create mode 100644 tests/test_select_gen.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3289dd0950..6b2ad2b895 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.2.0 + rev: v0.4.7 hooks: - id: ruff args: diff --git a/requirements-docs-tests.txt b/requirements-docs-tests.txt index 28f1ad1be9..c65317a7cf 100644 --- a/requirements-docs-tests.txt +++ b/requirements-docs-tests.txt @@ -1,2 +1,2 @@ # For mkdocstrings and code generator using templates -black >=22.10,<24.0 +black >=22.10 diff --git a/requirements-docs.txt b/requirements-docs.txt index cacb5dc2a3..d3a1838af8 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -12,7 +12,7 @@ pillow==10.1.0 # For image processing by Material for MkDocs cairosvg==2.7.0 mkdocstrings[python]==0.23.0 -griffe-typingdoc==0.2.2 +# Enable griffe-typingdoc once dropping Python 3.7 and upgrading typing-extensions +# griffe-typingdoc==0.2.5 # For griffe, it formats with black -black==23.3.0 typer == 0.12.3 diff --git a/requirements-tests.txt b/requirements-tests.txt index 3c2578e9c5..8801bb9b91 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -3,7 +3,7 @@ pytest >=7.0.1,<8.0.0 coverage[toml] >=6.2,<8.0 mypy ==1.4.1 -ruff ==0.2.0 +ruff ==0.4.7 # For FastAPI tests fastapi >=0.103.2 httpx ==0.24.1 diff --git a/scripts/test.sh b/scripts/test.sh index 1460a9c7ec..9b758bdbdf 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -3,7 +3,6 @@ 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/ext/asyncio/session.py b/sqlmodel/ext/asyncio/session.py index 012d8ef5e4..467d0bd84e 100644 --- a/sqlmodel/ext/asyncio/session.py +++ b/sqlmodel/ext/asyncio/session.py @@ -43,8 +43,7 @@ async def exec( bind_arguments: Optional[Dict[str, Any]] = None, _parent_execute_state: Optional[Any] = None, _add_event: Optional[Any] = None, - ) -> TupleResult[_TSelectParam]: - ... + ) -> TupleResult[_TSelectParam]: ... @overload async def exec( @@ -56,8 +55,7 @@ async def exec( bind_arguments: Optional[Dict[str, Any]] = None, _parent_execute_state: Optional[Any] = None, _add_event: Optional[Any] = None, - ) -> ScalarResult[_TSelectParam]: - ... + ) -> ScalarResult[_TSelectParam]: ... async def exec( self, diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 40051a522c..505683f756 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -231,8 +231,7 @@ def Field( sa_column_args: Union[Sequence[Any], UndefinedType] = Undefined, sa_column_kwargs: Union[Mapping[str, Any], UndefinedType] = Undefined, schema_extra: Optional[Dict[str, Any]] = None, -) -> Any: - ... +) -> Any: ... @overload @@ -268,8 +267,7 @@ def Field( repr: bool = True, sa_column: Union[Column, UndefinedType] = Undefined, # type: ignore schema_extra: Optional[Dict[str, Any]] = None, -) -> Any: - ... +) -> Any: ... def Field( @@ -361,8 +359,7 @@ def Relationship( link_model: Optional[Any] = None, sa_relationship_args: Optional[Sequence[Any]] = None, sa_relationship_kwargs: Optional[Mapping[str, Any]] = None, -) -> Any: - ... +) -> Any: ... @overload @@ -371,8 +368,7 @@ def Relationship( back_populates: Optional[str] = None, link_model: Optional[Any] = None, sa_relationship: Optional[RelationshipProperty[Any]] = None, -) -> Any: - ... +) -> Any: ... def Relationship( diff --git a/sqlmodel/orm/session.py b/sqlmodel/orm/session.py index e404bb137d..b60875095b 100644 --- a/sqlmodel/orm/session.py +++ b/sqlmodel/orm/session.py @@ -35,8 +35,7 @@ def exec( bind_arguments: Optional[Dict[str, Any]] = None, _parent_execute_state: Optional[Any] = None, _add_event: Optional[Any] = None, - ) -> TupleResult[_TSelectParam]: - ... + ) -> TupleResult[_TSelectParam]: ... @overload def exec( @@ -48,8 +47,7 @@ def exec( bind_arguments: Optional[Dict[str, Any]] = None, _parent_execute_state: Optional[Any] = None, _add_event: Optional[Any] = None, - ) -> ScalarResult[_TSelectParam]: - ... + ) -> ScalarResult[_TSelectParam]: ... def exec( self, diff --git a/sqlmodel/sql/_expression_select_gen.py b/sqlmodel/sql/_expression_select_gen.py index b6c15742fa..08aa59ad61 100644 --- a/sqlmodel/sql/_expression_select_gen.py +++ b/sqlmodel/sql/_expression_select_gen.py @@ -111,8 +111,7 @@ @overload -def select(__ent0: _TCCA[_T0]) -> SelectOfScalar[_T0]: - ... +def select(__ent0: _TCCA[_T0]) -> SelectOfScalar[_T0]: ... @overload @@ -127,32 +126,28 @@ def select(__ent0: _TScalar_0) -> SelectOfScalar[_TScalar_0]: # type: ignore def select( # type: ignore __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], -) -> Select[Tuple[_T0, _T1]]: - ... +) -> Select[Tuple[_T0, _T1]]: ... @overload def select( # type: ignore __ent0: _TCCA[_T0], entity_1: _TScalar_1, -) -> Select[Tuple[_T0, _TScalar_1]]: - ... +) -> Select[Tuple[_T0, _TScalar_1]]: ... @overload def select( # type: ignore entity_0: _TScalar_0, __ent1: _TCCA[_T1], -) -> Select[Tuple[_TScalar_0, _T1]]: - ... +) -> Select[Tuple[_TScalar_0, _T1]]: ... @overload def select( # type: ignore entity_0: _TScalar_0, entity_1: _TScalar_1, -) -> Select[Tuple[_TScalar_0, _TScalar_1]]: - ... +) -> Select[Tuple[_TScalar_0, _TScalar_1]]: ... @overload @@ -160,8 +155,7 @@ def select( # type: ignore __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2], -) -> Select[Tuple[_T0, _T1, _T2]]: - ... +) -> Select[Tuple[_T0, _T1, _T2]]: ... @overload @@ -169,8 +163,7 @@ def select( # type: ignore __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], entity_2: _TScalar_2, -) -> Select[Tuple[_T0, _T1, _TScalar_2]]: - ... +) -> Select[Tuple[_T0, _T1, _TScalar_2]]: ... @overload @@ -178,8 +171,7 @@ def select( # type: ignore __ent0: _TCCA[_T0], entity_1: _TScalar_1, __ent2: _TCCA[_T2], -) -> Select[Tuple[_T0, _TScalar_1, _T2]]: - ... +) -> Select[Tuple[_T0, _TScalar_1, _T2]]: ... @overload @@ -187,8 +179,7 @@ def select( # type: ignore __ent0: _TCCA[_T0], entity_1: _TScalar_1, entity_2: _TScalar_2, -) -> Select[Tuple[_T0, _TScalar_1, _TScalar_2]]: - ... +) -> Select[Tuple[_T0, _TScalar_1, _TScalar_2]]: ... @overload @@ -196,8 +187,7 @@ def select( # type: ignore entity_0: _TScalar_0, __ent1: _TCCA[_T1], __ent2: _TCCA[_T2], -) -> Select[Tuple[_TScalar_0, _T1, _T2]]: - ... +) -> Select[Tuple[_TScalar_0, _T1, _T2]]: ... @overload @@ -205,8 +195,7 @@ def select( # type: ignore entity_0: _TScalar_0, __ent1: _TCCA[_T1], entity_2: _TScalar_2, -) -> Select[Tuple[_TScalar_0, _T1, _TScalar_2]]: - ... +) -> Select[Tuple[_TScalar_0, _T1, _TScalar_2]]: ... @overload @@ -214,8 +203,7 @@ def select( # type: ignore entity_0: _TScalar_0, entity_1: _TScalar_1, __ent2: _TCCA[_T2], -) -> Select[Tuple[_TScalar_0, _TScalar_1, _T2]]: - ... +) -> Select[Tuple[_TScalar_0, _TScalar_1, _T2]]: ... @overload @@ -223,8 +211,7 @@ def select( # type: ignore entity_0: _TScalar_0, entity_1: _TScalar_1, entity_2: _TScalar_2, -) -> Select[Tuple[_TScalar_0, _TScalar_1, _TScalar_2]]: - ... +) -> Select[Tuple[_TScalar_0, _TScalar_1, _TScalar_2]]: ... @overload @@ -233,8 +220,7 @@ def select( # type: ignore __ent1: _TCCA[_T1], __ent2: _TCCA[_T2], __ent3: _TCCA[_T3], -) -> Select[Tuple[_T0, _T1, _T2, _T3]]: - ... +) -> Select[Tuple[_T0, _T1, _T2, _T3]]: ... @overload @@ -243,8 +229,7 @@ def select( # type: ignore __ent1: _TCCA[_T1], __ent2: _TCCA[_T2], entity_3: _TScalar_3, -) -> Select[Tuple[_T0, _T1, _T2, _TScalar_3]]: - ... +) -> Select[Tuple[_T0, _T1, _T2, _TScalar_3]]: ... @overload @@ -253,8 +238,7 @@ def select( # type: ignore __ent1: _TCCA[_T1], entity_2: _TScalar_2, __ent3: _TCCA[_T3], -) -> Select[Tuple[_T0, _T1, _TScalar_2, _T3]]: - ... +) -> Select[Tuple[_T0, _T1, _TScalar_2, _T3]]: ... @overload @@ -263,8 +247,7 @@ def select( # type: ignore __ent1: _TCCA[_T1], entity_2: _TScalar_2, entity_3: _TScalar_3, -) -> Select[Tuple[_T0, _T1, _TScalar_2, _TScalar_3]]: - ... +) -> Select[Tuple[_T0, _T1, _TScalar_2, _TScalar_3]]: ... @overload @@ -273,8 +256,7 @@ def select( # type: ignore entity_1: _TScalar_1, __ent2: _TCCA[_T2], __ent3: _TCCA[_T3], -) -> Select[Tuple[_T0, _TScalar_1, _T2, _T3]]: - ... +) -> Select[Tuple[_T0, _TScalar_1, _T2, _T3]]: ... @overload @@ -283,8 +265,7 @@ def select( # type: ignore entity_1: _TScalar_1, __ent2: _TCCA[_T2], entity_3: _TScalar_3, -) -> Select[Tuple[_T0, _TScalar_1, _T2, _TScalar_3]]: - ... +) -> Select[Tuple[_T0, _TScalar_1, _T2, _TScalar_3]]: ... @overload @@ -293,8 +274,7 @@ def select( # type: ignore entity_1: _TScalar_1, entity_2: _TScalar_2, __ent3: _TCCA[_T3], -) -> Select[Tuple[_T0, _TScalar_1, _TScalar_2, _T3]]: - ... +) -> Select[Tuple[_T0, _TScalar_1, _TScalar_2, _T3]]: ... @overload @@ -303,8 +283,7 @@ def select( # type: ignore entity_1: _TScalar_1, entity_2: _TScalar_2, entity_3: _TScalar_3, -) -> Select[Tuple[_T0, _TScalar_1, _TScalar_2, _TScalar_3]]: - ... +) -> Select[Tuple[_T0, _TScalar_1, _TScalar_2, _TScalar_3]]: ... @overload @@ -313,8 +292,7 @@ def select( # type: ignore __ent1: _TCCA[_T1], __ent2: _TCCA[_T2], __ent3: _TCCA[_T3], -) -> Select[Tuple[_TScalar_0, _T1, _T2, _T3]]: - ... +) -> Select[Tuple[_TScalar_0, _T1, _T2, _T3]]: ... @overload @@ -323,8 +301,7 @@ def select( # type: ignore __ent1: _TCCA[_T1], __ent2: _TCCA[_T2], entity_3: _TScalar_3, -) -> Select[Tuple[_TScalar_0, _T1, _T2, _TScalar_3]]: - ... +) -> Select[Tuple[_TScalar_0, _T1, _T2, _TScalar_3]]: ... @overload @@ -333,8 +310,7 @@ def select( # type: ignore __ent1: _TCCA[_T1], entity_2: _TScalar_2, __ent3: _TCCA[_T3], -) -> Select[Tuple[_TScalar_0, _T1, _TScalar_2, _T3]]: - ... +) -> Select[Tuple[_TScalar_0, _T1, _TScalar_2, _T3]]: ... @overload @@ -343,8 +319,7 @@ def select( # type: ignore __ent1: _TCCA[_T1], entity_2: _TScalar_2, entity_3: _TScalar_3, -) -> Select[Tuple[_TScalar_0, _T1, _TScalar_2, _TScalar_3]]: - ... +) -> Select[Tuple[_TScalar_0, _T1, _TScalar_2, _TScalar_3]]: ... @overload @@ -353,8 +328,7 @@ def select( # type: ignore entity_1: _TScalar_1, __ent2: _TCCA[_T2], __ent3: _TCCA[_T3], -) -> Select[Tuple[_TScalar_0, _TScalar_1, _T2, _T3]]: - ... +) -> Select[Tuple[_TScalar_0, _TScalar_1, _T2, _T3]]: ... @overload @@ -363,8 +337,7 @@ def select( # type: ignore entity_1: _TScalar_1, __ent2: _TCCA[_T2], entity_3: _TScalar_3, -) -> Select[Tuple[_TScalar_0, _TScalar_1, _T2, _TScalar_3]]: - ... +) -> Select[Tuple[_TScalar_0, _TScalar_1, _T2, _TScalar_3]]: ... @overload @@ -373,8 +346,7 @@ def select( # type: ignore entity_1: _TScalar_1, entity_2: _TScalar_2, __ent3: _TCCA[_T3], -) -> Select[Tuple[_TScalar_0, _TScalar_1, _TScalar_2, _T3]]: - ... +) -> Select[Tuple[_TScalar_0, _TScalar_1, _TScalar_2, _T3]]: ... @overload @@ -383,8 +355,7 @@ def select( # type: ignore entity_1: _TScalar_1, entity_2: _TScalar_2, entity_3: _TScalar_3, -) -> Select[Tuple[_TScalar_0, _TScalar_1, _TScalar_2, _TScalar_3]]: - ... +) -> Select[Tuple[_TScalar_0, _TScalar_1, _TScalar_2, _TScalar_3]]: ... # Generated overloads end diff --git a/sqlmodel/sql/_expression_select_gen.py.jinja2 b/sqlmodel/sql/_expression_select_gen.py.jinja2 index 307e32b784..ef838e4168 100644 --- a/sqlmodel/sql/_expression_select_gen.py.jinja2 +++ b/sqlmodel/sql/_expression_select_gen.py.jinja2 @@ -56,8 +56,7 @@ _T{{ i }} = TypeVar("_T{{ i }}") # Generated TypeVars end @overload -def select(__ent0: _TCCA[_T0]) -> SelectOfScalar[_T0]: - ... +def select(__ent0: _TCCA[_T0]) -> SelectOfScalar[_T0]: ... @overload @@ -72,8 +71,7 @@ def select(__ent0: _TScalar_0) -> SelectOfScalar[_TScalar_0]: # type: ignore @overload def select( # type: ignore {% for arg in signature[0] %}{{ arg.name }}: {{ arg.annotation }}, {% endfor %} - ) -> Select[Tuple[{%for ret in signature[1] %}{{ ret }} {% if not loop.last %}, {% endif %}{% endfor %}]]: - ... + ) -> Select[Tuple[{%for ret in signature[1] %}{{ ret }} {% if not loop.last %}, {% endif %}{% endfor %}]]: ... {% endfor %} diff --git a/tests/test_select_gen.py b/tests/test_select_gen.py new file mode 100644 index 0000000000..6d578f7708 --- /dev/null +++ b/tests/test_select_gen.py @@ -0,0 +1,19 @@ +import subprocess +import sys +from pathlib import Path + +from .conftest import needs_py39 + +root_path = Path(__file__).parent.parent + + +@needs_py39 +def test_select_gen() -> None: + result = subprocess.run( + [sys.executable, "scripts/generate_select.py"], + env={"CHECK_JINJA": "1"}, + check=True, + cwd=root_path, + capture_output=True, + ) + print(result.stdout) From 883cbe3a8dc7da3ab44d57cbec7828cf93d876a7 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 4 Jun 2024 03:22:28 +0000 Subject: [PATCH 35/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 1acd807efe..d479b9b20f 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -21,6 +21,7 @@ ### Internal +* ⬆️ Upgrade Ruff and Black. PR [#968](https://github.com/tiangolo/sqlmodel/pull/968) by [@tiangolo](https://github.com/tiangolo). * ⬆ Bump tiangolo/issue-manager from 0.4.1 to 0.5.0. PR [#922](https://github.com/tiangolo/sqlmodel/pull/922) by [@dependabot[bot]](https://github.com/apps/dependabot). * 📌 Pin typing-extensions in tests for compatiblity with Python 3.8, dirty-equals, Pydantic. PR [#965](https://github.com/tiangolo/sqlmodel/pull/965) by [@tiangolo](https://github.com/tiangolo). * 👷 Update GitHub Actions to download and upload artifacts. PR [#936](https://github.com/tiangolo/sqlmodel/pull/936) by [@tiangolo](https://github.com/tiangolo). From 4590963e884e7f112a2fd28a6c3396b6570dc58d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 3 Jun 2024 22:26:53 -0500 Subject: [PATCH 36/46] =?UTF-8?q?=F0=9F=94=96=20Release=20version=200.0.19?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 2 ++ sqlmodel/__init__.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index d479b9b20f..57e6a0f652 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,8 @@ ## Latest Changes +## 0.0.19 + ### Fixes * 🐛 Fix pydantic `EmailStr` support and `max_length` in several String subclasses. PR [#966](https://github.com/tiangolo/sqlmodel/pull/966) by [@estebanx64](https://github.com/estebanx64). diff --git a/sqlmodel/__init__.py b/sqlmodel/__init__.py index 397c07f5d2..61ae35f7eb 100644 --- a/sqlmodel/__init__.py +++ b/sqlmodel/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.0.18" +__version__ = "0.0.19" # Re-export from SQLAlchemy from sqlalchemy.engine import create_engine as create_engine From f6ad19b1a7f0e23084ffd53091b0aad8df08add8 Mon Sep 17 00:00:00 2001 From: Alejandra <90076947+alejsdev@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:48:02 -0500 Subject: [PATCH 37/46] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Update=20pip=20insta?= =?UTF-8?q?llation=20command=20in=20tutorial=20(#975)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/tutorial/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/index.md b/docs/tutorial/index.md index 9b0939b0c1..88d952a746 100644 --- a/docs/tutorial/index.md +++ b/docs/tutorial/index.md @@ -192,7 +192,7 @@ Now, after making sure we are inside of a virtual environment in some way, we ca
```console -# (env) $$ python -m pip install sqlmodel +# (env) $$ pip install sqlmodel ---> 100% Successfully installed sqlmodel pydantic sqlalchemy ``` From 8703539bf0753d1411a4f6abdaa41c058161bd28 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 4 Jun 2024 23:48:20 +0000 Subject: [PATCH 38/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 57e6a0f652..e545245592 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,10 @@ ## Latest Changes +### Docs + +* ✏️ Update pip installation command in tutorial. PR [#975](https://github.com/tiangolo/sqlmodel/pull/975) by [@alejsdev](https://github.com/alejsdev). + ## 0.0.19 ### Fixes From e7c62fc9d9c1ab3287c6ee06c742f96ed299a7f0 Mon Sep 17 00:00:00 2001 From: Anderson T Date: Tue, 4 Jun 2024 16:56:52 -0700 Subject: [PATCH 39/46] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20typo=20in=20`s?= =?UTF-8?q?qlmodel/=5Fcompat.py`=20(#950)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sqlmodel/_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlmodel/_compat.py b/sqlmodel/_compat.py index d42a62429e..4018d1bb39 100644 --- a/sqlmodel/_compat.py +++ b/sqlmodel/_compat.py @@ -194,7 +194,7 @@ def get_type_from_field(field: Any) -> Any: # Non optional unions are not allowed if bases[0] is not NoneType and bases[1] is not NoneType: raise ValueError( - "Cannot have a (non-optional) union as a SQLlchemy field" + "Cannot have a (non-optional) union as a SQLAlchemy field" ) # Optional unions are allowed return bases[0] if bases[0] is not NoneType else bases[1] From 6e7e5539637bb968c517a15ae83a233e618afd17 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 4 Jun 2024 23:57:10 +0000 Subject: [PATCH 40/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index e545245592..a331c22e31 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -4,6 +4,7 @@ ### Docs +* ✏️ Fix typo in `sqlmodel/_compat.py`. PR [#950](https://github.com/tiangolo/sqlmodel/pull/950) by [@Highfire1](https://github.com/Highfire1). * ✏️ Update pip installation command in tutorial. PR [#975](https://github.com/tiangolo/sqlmodel/pull/975) by [@alejsdev](https://github.com/alejsdev). ## 0.0.19 From 24e76c7a1386e00a9aafa155fa58e310c56ef560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mieszko=20Ba=C5=84czerowski?= Date: Wed, 5 Jun 2024 01:58:27 +0200 Subject: [PATCH 41/46] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20typo=20in=20`d?= =?UTF-8?q?ocs/tutorial/relationship-attributes/index.md`=20(#880)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/tutorial/relationship-attributes/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/relationship-attributes/index.md b/docs/tutorial/relationship-attributes/index.md index b32fb637df..f63b2669e3 100644 --- a/docs/tutorial/relationship-attributes/index.md +++ b/docs/tutorial/relationship-attributes/index.md @@ -4,7 +4,7 @@ In the previous chapters we discussed how to manage databases with tables that h And then we read the data together with `select()` and using `.where()` or `.join()` to connect it. -Now we will see how to use **Relationship Attributes**, an extra feature of **SQLModel** (and SQLAlchemy) to work with the data in the database in way much more familiar way, and closer to normal Python code. +Now we will see how to use **Relationship Attributes**, an extra feature of **SQLModel** (and SQLAlchemy), to work with the data in the database in a much more familiar way, and closer to normal Python code. /// info From 23869cab0d2e50f4abab8dce228180fe7647083e Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 4 Jun 2024 23:58:51 +0000 Subject: [PATCH 42/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index a331c22e31..82d77a97f8 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,8 @@ ## Latest Changes +* ✏️ Fix typo in `docs/tutorial/relationship-attributes/index.md`. PR [#880](https://github.com/tiangolo/sqlmodel/pull/880) by [@UncleGoogle](https://github.com/UncleGoogle). + ### Docs * ✏️ Fix typo in `sqlmodel/_compat.py`. PR [#950](https://github.com/tiangolo/sqlmodel/pull/950) by [@Highfire1](https://github.com/Highfire1). From 1263024be557e0024cb4b6f8565b53ab8359cecf Mon Sep 17 00:00:00 2001 From: Lucien O <36520137+luco17@users.noreply.github.com> Date: Wed, 5 Jun 2024 01:00:14 +0100 Subject: [PATCH 43/46] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20typo=20in=20`d?= =?UTF-8?q?ocs/tutorial`=20(#943)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/tutorial/create-db-and-table.md | 4 ++-- docs/tutorial/indexes.md | 2 +- docs/tutorial/select.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/tutorial/create-db-and-table.md b/docs/tutorial/create-db-and-table.md index d87b935a1c..f508cf7cea 100644 --- a/docs/tutorial/create-db-and-table.md +++ b/docs/tutorial/create-db-and-table.md @@ -354,7 +354,7 @@ But we will talk about it later. ### Engine Database URL -Each supported database has it's own URL type. For example, for **SQLite** it is `sqlite:///` followed by the file path. For example: +Each supported database has its own URL type. For example, for **SQLite** it is `sqlite:///` followed by the file path. For example: * `sqlite:///database.db` * `sqlite:///databases/local/application.db` @@ -470,7 +470,7 @@ If you didn't know about SQLAlchemy before and are just learning **SQLModel**, y You can read a lot more about the engine in the SQLAlchemy documentation. -**SQLModel** defines it's own `create_engine()` function. It is the same as SQLAlchemy's `create_engine()`, but with the difference that it defaults to use `future=True` (which means that it uses the style of the latest SQLAlchemy, 1.4, and the future 2.0). +**SQLModel** defines its own `create_engine()` function. It is the same as SQLAlchemy's `create_engine()`, but with the difference that it defaults to use `future=True` (which means that it uses the style of the latest SQLAlchemy, 1.4, and the future 2.0). And SQLModel's version of `create_engine()` is type annotated internally, so your editor will be able to help you with autocompletion and inline errors. diff --git a/docs/tutorial/indexes.md b/docs/tutorial/indexes.md index d0854720cf..d0724f5183 100644 --- a/docs/tutorial/indexes.md +++ b/docs/tutorial/indexes.md @@ -90,7 +90,7 @@ Do you like **fancy words**? Cool! Programmers tend to like fancy words. 😅 That algorithm I showed you above is called **Binary Search**. -It's called like that because you **search** something by splitting the dictionary (or any ordered list of things) in **two** ("binary" means "two") parts. And you do that process multiple times until you find what you want. +It's called that because you **search** something by splitting the dictionary (or any ordered list of things) in **two** ("binary" means "two") parts. And you do that process multiple times until you find what you want. /// diff --git a/docs/tutorial/select.md b/docs/tutorial/select.md index 47c80a2661..be66de351e 100644 --- a/docs/tutorial/select.md +++ b/docs/tutorial/select.md @@ -713,7 +713,7 @@ In this chapter we are touching some of them. When importing from `sqlmodel` the `select()` function, you are using **SQLModel**'s version of `select`. -SQLAchemy also has it's own `select`, and SQLModel's `select` uses SQLAlchemy's `select` internally. +SQLAchemy also has its own `select`, and SQLModel's `select` uses SQLAlchemy's `select` internally. But SQLModel's version does a lot of **tricks** with type annotations to make sure you get the best **editor support** possible, no matter if you use **VS Code**, **PyCharm**, or something else. ✨ From f1bfebc9e2dc5e637c87390c0f005129050d9d6a Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 5 Jun 2024 00:00:30 +0000 Subject: [PATCH 44/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 82d77a97f8..60453f71e7 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -6,6 +6,7 @@ ### Docs +* ✏️ Fix typo in `docs/tutorial`. PR [#943](https://github.com/tiangolo/sqlmodel/pull/943) by [@luco17](https://github.com/luco17). * ✏️ Fix typo in `sqlmodel/_compat.py`. PR [#950](https://github.com/tiangolo/sqlmodel/pull/950) by [@Highfire1](https://github.com/Highfire1). * ✏️ Update pip installation command in tutorial. PR [#975](https://github.com/tiangolo/sqlmodel/pull/975) by [@alejsdev](https://github.com/alejsdev). From 8416508d7952a0bf43b2c321cad28372a81cf819 Mon Sep 17 00:00:00 2001 From: Alejandra <90076947+alejsdev@users.noreply.github.com> Date: Tue, 4 Jun 2024 20:52:36 -0500 Subject: [PATCH 45/46] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Add=20missing=20step?= =?UTF-8?q?=20in=20`create-db-and-table-with-db-browser.md`=20(#976)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/tutorial/create-db-and-table-with-db-browser.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/tutorial/create-db-and-table-with-db-browser.md b/docs/tutorial/create-db-and-table-with-db-browser.md index 72be6db297..b3a6f960ae 100644 --- a/docs/tutorial/create-db-and-table-with-db-browser.md +++ b/docs/tutorial/create-db-and-table-with-db-browser.md @@ -125,6 +125,8 @@ And delete that `./database.db` file in your project directory. And click again on New Database. +Save the file with the name `database.db` again. + This time, if you see the dialog to create a new table, just close it by clicking the Cancel button. And now, go to the tab Execute SQL. From 96bfd855f8bd1f5250385155072686ce1b6a3ff2 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 5 Jun 2024 01:52:54 +0000 Subject: [PATCH 46/46] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 60453f71e7..7101d4aba6 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -6,6 +6,7 @@ ### Docs +* ✏️ Add missing step in `create-db-and-table-with-db-browser.md`. PR [#976](https://github.com/tiangolo/sqlmodel/pull/976) by [@alejsdev](https://github.com/alejsdev). * ✏️ Fix typo in `docs/tutorial`. PR [#943](https://github.com/tiangolo/sqlmodel/pull/943) by [@luco17](https://github.com/luco17). * ✏️ Fix typo in `sqlmodel/_compat.py`. PR [#950](https://github.com/tiangolo/sqlmodel/pull/950) by [@Highfire1](https://github.com/Highfire1). * ✏️ Update pip installation command in tutorial. PR [#975](https://github.com/tiangolo/sqlmodel/pull/975) by [@alejsdev](https://github.com/alejsdev).