]> git.ipfire.org Git - thirdparty/fastapi/sqlmodel.git/commitdiff
🗑️ Deprecate Python 3.6 and upgrade Poetry and Poetry Version Plugin (#627)
authorSebastián Ramírez <tiangolo@gmail.com>
Sat, 29 Jul 2023 10:32:47 +0000 (12:32 +0200)
committerGitHub <noreply@github.com>
Sat, 29 Jul 2023 10:32:47 +0000 (12:32 +0200)
14 files changed:
.github/workflows/build-docs.yml
.github/workflows/publish.yml
.github/workflows/test.yml
README.md
docs/contributing.md
docs/features.md
docs/index.md
docs/tutorial/index.md
pyproject.toml
scripts/lint.sh
scripts/test.sh
sqlmodel/main.py
sqlmodel/sql/expression.py
sqlmodel/sql/expression.py.jinja2

index 6400691533950d87a25ac2afcd2e6f412090b77c..3bcc78dfa5983780b533701121dbc7e64056b153 100644 (file)
@@ -4,26 +4,28 @@ on:
     branches:
       - main
   pull_request:
-    types: [opened, synchronize]
+    types:
+      - opened
+      - synchronize
   workflow_dispatch:
     inputs:
       debug_enabled:
-        description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'     
+        description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
         required: false
         default: false
 jobs:
   build-docs:
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-latest
     steps:
       - name: Dump GitHub context
         env:
           GITHUB_CONTEXT: ${{ toJson(github) }}
         run: echo "$GITHUB_CONTEXT"
-      - uses: actions/checkout@v3.1.0
+      - uses: actions/checkout@v3
       - name: Set up Python
         uses: actions/setup-python@v4
         with:
-          python-version: "3.7"
+          python-version: "3.11"
       # Allow debugging with tmate
       - name: Setup tmate session
         uses: mxschmitt/action-tmate@v3
@@ -34,34 +36,30 @@ jobs:
         id: cache
         with:
           path: ${{ env.pythonLocation }}
-          key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root-docs
+          key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root-docs-v2
       - name: Install poetry
         if: steps.cache.outputs.cache-hit != 'true'
-        # TODO: remove python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
-        # once there's a release of Poetry 1.2.x including poetry-core > 1.1.0a6
-        # Ref: https://github.com/python-poetry/poetry-core/pull/188
         run: |
           python -m pip install --upgrade pip
-          python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
-          python -m pip install "poetry==1.2.0a2"
-          python -m poetry plugin add poetry-version-plugin
+          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: Install Material for MkDocs Insiders
-        if: github.event.pull_request.head.repo.fork == false && steps.cache.outputs.cache-hit != 'true'
-        run: python -m poetry run pip install git+https://${{ secrets.ACTIONS_TOKEN }}@github.com/squidfunk/mkdocs-material-insiders.git
+        if: ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false ) && 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
       - uses: actions/cache@v3
         with:
           key: mkdocs-cards-${{ github.ref }}
           path: .cache
       - name: Build Docs
-        if: github.event.pull_request.head.repo.fork == true
+        if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true
         run: python -m poetry run mkdocs build
       - name: Build Docs with Insiders
-        if: github.event.pull_request.head.repo.fork == false
+        if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false
         run: python -m poetry run mkdocs build --config-file mkdocs.insiders.yml
       - name: Zip docs
         run: python -m poetry run bash ./scripts/zip-docs.sh
@@ -70,7 +68,7 @@ jobs:
           name: docs-zip
           path: ./site/docs.zip
       - name: Deploy to Netlify
-        uses: nwtgck/actions-netlify@v1.1.5
+        uses: nwtgck/actions-netlify@v2.0.0
         with:
           publish-dir: './site'
           production-branch: main
index f3c1e980a61de2e6b3fec9875a90b0cd2e37a9fb..d7884111cb313e3c8a96e8d5a1f743b925c30375 100644 (file)
@@ -7,15 +7,15 @@ on:
   workflow_dispatch:
     inputs:
       debug_enabled:
-        description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'     
+        description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
         required: false
         default: false
 
 jobs:
   publish:
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v3.1.0
+      - uses: actions/checkout@v3
       - name: Set up Python
         uses: actions/setup-python@v4
         with:
@@ -30,17 +30,13 @@ jobs:
         id: cache
         with:
           path: ${{ env.pythonLocation }}
-          key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root
+          key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root-v2
       - name: Install poetry
         if: steps.cache.outputs.cache-hit != 'true'
-        # TODO: remove python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
-        # once there's a release of Poetry 1.2.x including poetry-core > 1.1.0a6
-        # Ref: https://github.com/python-poetry/poetry-core/pull/188
         run: |
           python -m pip install --upgrade pip
-          python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
-          python -m pip install "poetry==1.2.0a2"
-          python -m poetry plugin add poetry-version-plugin
+          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
index 585ffc0455dac936d6518c3d50d4022b36929cef..c7435325a7e1e0756803048f3ae9ce4a27c6c194 100644 (file)
@@ -5,24 +5,30 @@ on:
     branches:
       - main
   pull_request:
-    types: [opened, synchronize]
+    types:
+      - opened
+      - synchronize
   workflow_dispatch:
     inputs:
       debug_enabled:
-        description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'     
+        description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
         required: false
         default: false
 
 jobs:
   test:
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: ["3.6.15", "3.7", "3.8", "3.9", "3.10"]
+        python-version:
+          - "3.7"
+          - "3.8"
+          - "3.9"
+          - "3.10"
       fail-fast: false
 
     steps:
-      - uses: actions/checkout@v3.1.0
+      - uses: actions/checkout@v3
       - name: Set up Python
         uses: actions/setup-python@v4
         with:
@@ -37,24 +43,19 @@ jobs:
         id: cache
         with:
           path: ${{ env.pythonLocation }}
-          key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root
+          key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root-v2
       - name: Install poetry
         if: steps.cache.outputs.cache-hit != 'true'
-        # TODO: remove python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
-        # once there's a release of Poetry 1.2.x including poetry-core > 1.1.0a6
-        # Ref: https://github.com/python-poetry/poetry-core/pull/188
         run: |
           python -m pip install --upgrade pip
-          python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
-          python -m pip install "poetry==1.2.0a2"
-          python -m poetry plugin add poetry-version-plugin
+          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: Lint
-        if: ${{ matrix.python-version != '3.6.15' }}
         run: python -m poetry run bash scripts/lint.sh
       - run: mkdir coverage
       - name: Test
@@ -68,7 +69,8 @@ jobs:
           name: coverage
           path: coverage
   coverage-combine:
-    needs: [test]
+    needs:
+      - test
     runs-on: ubuntu-latest
 
     steps:
@@ -96,3 +98,15 @@ jobs:
         with:
           name: coverage-html
           path: htmlcov
+
+  # https://github.com/marketplace/actions/alls-green#why
+  alls-green:  # This job does nothing and is only used for the branch protection
+    if: always()
+    needs:
+      - coverage-combine
+    runs-on: ubuntu-latest
+    steps:
+      - name: Decide whether the needed jobs succeeded or failed
+        uses: re-actors/alls-green@release/v1
+        with:
+          jobs: ${{ toJSON(needs) }}
index 5721f1cdb0c3fca170cc1c09e4f265c999c96133..f85bb97a6db0a3cf2cd1888107063f325b3e0ea5 100644 (file)
--- a/README.md
+++ b/README.md
@@ -50,7 +50,7 @@ It combines SQLAlchemy and Pydantic and tries to simplify the code you write as
 
 ## Requirements
 
-A recent and currently supported version of Python (right now, <a href="https://www.python.org/downloads/" class="external-link" target="_blank">Python supports versions 3.6 and above</a>).
+A recent and currently supported <a href="https://www.python.org/downloads/" class="external-link" target="_blank">version of Python Python</a>.
 
 As **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.
 
index f2964fba9b7bca2b8b89ec430a2065d639777578..1cd62d42dcfb5b273280b8775b845418105bc6a6 100644 (file)
@@ -6,10 +6,6 @@ First, you might want to see the basic ways to [help SQLModel and get help](help
 
 If you already cloned the repository and you know that you need to deep dive in the code, here are some guidelines to set up your environment.
 
-### Python
-
-SQLModel supports Python 3.6 and above, but for development you should have at least **Python 3.7**.
-
 ### Poetry
 
 **SQLModel** uses <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a> to build, package, and publish the project.
index 09de0c17f9c4c6ed838a84d57d6e7ee23c80509b..102edef72541c73d6dfe69a6c6d0b6c8918c568b 100644 (file)
@@ -12,7 +12,7 @@ Nevertheless, SQLModel is completely **independent** of FastAPI and can be used
 
 ## Just Modern Python
 
-It's all based on standard <abbr title="Python currently supported versions, 3.6 and above.">modern **Python**</abbr> type annotations. No new syntax to learn. Just standard modern Python.
+It's all based on standard <abbr title="Currently supported versions of Python">modern **Python**</abbr> type annotations. No new syntax to learn. Just standard modern Python.
 
 If you need a 2 minute refresher of how to use Python types (even if you don't use SQLModel or FastAPI), check the FastAPI tutorial section: <a href="https://fastapi.tiangolo.com/python-types/" class="external-link" target="_blank">Python types intro</a>.
 
index 5721f1cdb0c3fca170cc1c09e4f265c999c96133..f85bb97a6db0a3cf2cd1888107063f325b3e0ea5 100644 (file)
@@ -50,7 +50,7 @@ It combines SQLAlchemy and Pydantic and tries to simplify the code you write as
 
 ## Requirements
 
-A recent and currently supported version of Python (right now, <a href="https://www.python.org/downloads/" class="external-link" target="_blank">Python supports versions 3.6 and above</a>).
+A recent and currently supported <a href="https://www.python.org/downloads/" class="external-link" target="_blank">version of Python Python</a>.
 
 As **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.
 
index 33cf6226c4631abc75d238abd75df12a810ad38e..79fa670cfd456e953065885eba1ce69284ac7556 100644 (file)
@@ -64,15 +64,13 @@ $ cd sqlmodel-tutorial
 
 Make sure you have an officially supported version of Python.
 
-Currently it is **Python 3.6** and above (Python 3.5 was already deprecated).
-
 You can check which version you have with:
 
 <div class="termy">
 
 ```console
 $ python3 --version
-Python 3.6.9
+Python 3.11
 ```
 
 </div>
@@ -84,8 +82,6 @@ You might want to try with the specific versions, for example with:
 * `python3.10`
 * `python3.9`
 * `python3.8`
-* `python3.7`
-* `python3.6`
 
 The code would look like this:
 
index e3b1d3c2795ac2bfc5303841faef0fa9318be3af..e4027271507db8eedfea7082b7cc393caea0a395 100644 (file)
@@ -17,10 +17,10 @@ classifiers = [
     "Intended Audience :: System Administrators",
     "License :: OSI Approved :: MIT License",
     "Programming Language :: Python :: 3 :: Only",
-    "Programming Language :: Python :: 3.6",
     "Programming Language :: Python :: 3.7",
     "Programming Language :: Python :: 3.8",
     "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
     "Topic :: Database",
     "Topic :: Database :: Database Engines/Servers",
     "Topic :: Internet",
@@ -30,7 +30,7 @@ classifiers = [
 ]
 
 [tool.poetry.dependencies]
-python = "^3.6.1"
+python = "^3.7"
 SQLAlchemy = ">=1.4.17,<=1.4.41"
 pydantic = "^1.8.2"
 sqlalchemy2-stubs = {version = "*", allow-prereleases = true}
@@ -39,19 +39,17 @@ sqlalchemy2-stubs = {version = "*", allow-prereleases = true}
 pytest = "^7.0.1"
 mypy = "0.971"
 flake8 = "^5.0.4"
-black = {version = "^22.10.0", python = "^3.7"}
+black = "^22.10.0"
 mkdocs = "^1.2.1"
 mkdocs-material = "^8.1.4"
-pillow = {version = "^9.3.0", python = "^3.7"}
-cairosvg = {version = "^2.5.2", python = "^3.7"}
+pillow = "^9.3.0"
+cairosvg = "^2.5.2"
 mdx-include = "^1.4.1"
 coverage = {extras = ["toml"], version = "^6.2"}
 fastapi = "^0.68.1"
 requests = "^2.26.0"
 autoflake = "^1.4"
 isort = "^5.9.3"
-async_generator = {version = "*", python = "~3.6"}
-async-exit-stack = {version = "*", python = "~3.6"}
 
 [build-system]
 requires = ["poetry-core"]
index 02568cda6b58560a9eed413b4338cf1eaf07bee7..4191d90f1f69bbf0539398ca06a6ab2934794664 100755 (executable)
@@ -7,5 +7,3 @@ mypy sqlmodel
 flake8 sqlmodel tests docs_src
 black sqlmodel tests docs_src --check
 isort sqlmodel tests docs_src scripts --check-only
-# TODO: move this to test.sh after deprecating Python 3.6
-CHECK_JINJA=1 python scripts/generate_select.py
index 9b758bdbdfc669c64a0bd2a34e3ba85f23750ac6..1460a9c7ecc20de9e77fcbad55990fccc70b28fa 100755 (executable)
@@ -3,6 +3,7 @@
 set -e
 set -x
 
+CHECK_JINJA=1 python scripts/generate_select.py
 coverage run -m pytest tests
 coverage combine
 coverage report --show-missing
index d95c4985073ffe72bff8262b59f86ea96bacaca1..5b5950a811d5a8b7dc770ee4a7a9c89afa08fa38 100644 (file)
@@ -11,6 +11,7 @@ from typing import (
     Callable,
     ClassVar,
     Dict,
+    ForwardRef,
     List,
     Mapping,
     Optional,
@@ -29,7 +30,7 @@ from pydantic.fields import SHAPE_SINGLETON
 from pydantic.fields import FieldInfo as PydanticFieldInfo
 from pydantic.fields import ModelField, Undefined, UndefinedType
 from pydantic.main import ModelMetaclass, validate_model
-from pydantic.typing import ForwardRef, NoArgAnyCallable, resolve_annotations
+from pydantic.typing import NoArgAnyCallable, resolve_annotations
 from pydantic.utils import ROOT_KEY, Representation
 from sqlalchemy import Boolean, Column, Date, DateTime
 from sqlalchemy import Enum as sa_Enum
index 31c0bc1a1e3b252646d8bed267797917ccbda364..264e39cba7cdf8d497f04c23af018f445ff064fb 100644 (file)
@@ -1,6 +1,5 @@
 # WARNING: do not modify this code, it is generated by expression.py.jinja2
 
-import sys
 from datetime import datetime
 from typing import (
     TYPE_CHECKING,
@@ -12,7 +11,6 @@ from typing import (
     Type,
     TypeVar,
     Union,
-    cast,
     overload,
 )
 from uuid import UUID
@@ -24,36 +22,17 @@ from sqlalchemy.sql.expression import Select as _Select
 
 _TSelect = TypeVar("_TSelect")
 
-# Workaround Generics incompatibility in Python 3.6
-# Ref: https://github.com/python/typing/issues/449#issuecomment-316061322
-if sys.version_info.minor >= 7:
 
-    class Select(_Select, Generic[_TSelect]):
-        inherit_cache = True
+class Select(_Select, Generic[_TSelect]):
+    inherit_cache = True
 
-    # This is not comparable to sqlalchemy.sql.selectable.ScalarSelect, that has a different
-    # purpose. This is the same as a normal SQLAlchemy Select class where there's only one
-    # entity, so the result will be converted to a scalar by default. This way writing
-    # for loops on the results will feel natural.
-    class SelectOfScalar(_Select, Generic[_TSelect]):
-        inherit_cache = True
 
-else:
-    from typing import GenericMeta  # type: ignore
-
-    class GenericSelectMeta(GenericMeta, _Select.__class__):  # type: ignore
-        pass
-
-    class _Py36Select(_Select, Generic[_TSelect], metaclass=GenericSelectMeta):
-        inherit_cache = True
-
-    class _Py36SelectOfScalar(_Select, Generic[_TSelect], metaclass=GenericSelectMeta):
-        inherit_cache = True
-
-    # Cast them for editors to work correctly, from several tricks tried, this works
-    # for both VS Code and PyCharm
-    Select = cast("Select", _Py36Select)  # type: ignore
-    SelectOfScalar = cast("SelectOfScalar", _Py36SelectOfScalar)  # type: ignore
+# This is not comparable to sqlalchemy.sql.selectable.ScalarSelect, that has a different
+# purpose. This is the same as a normal SQLAlchemy Select class where there's only one
+# entity, so the result will be converted to a scalar by default. This way writing
+# for loops on the results will feel natural.
+class SelectOfScalar(_Select, Generic[_TSelect]):
+    inherit_cache = True
 
 
 if TYPE_CHECKING:  # pragma: no cover
index 51f04a215d29d63785b4cd0ce4785fd5f668e12e..26d12a0395743eb1b5ca5e87e05125a08fe7d3bc 100644 (file)
@@ -1,4 +1,3 @@
-import sys
 from datetime import datetime
 from typing import (
     TYPE_CHECKING,
@@ -10,7 +9,6 @@ from typing import (
     Type,
     TypeVar,
     Union,
-    cast,
     overload,
 )
 from uuid import UUID
@@ -22,37 +20,15 @@ from sqlalchemy.sql.expression import Select as _Select
 
 _TSelect = TypeVar("_TSelect")
 
-# Workaround Generics incompatibility in Python 3.6
-# Ref: https://github.com/python/typing/issues/449#issuecomment-316061322
-if sys.version_info.minor >= 7:
-
-    class Select(_Select, Generic[_TSelect]):
-        inherit_cache = True
-
-    # This is not comparable to sqlalchemy.sql.selectable.ScalarSelect, that has a different
-    # purpose. This is the same as a normal SQLAlchemy Select class where there's only one
-    # entity, so the result will be converted to a scalar by default. This way writing
-    # for loops on the results will feel natural.
-    class SelectOfScalar(_Select, Generic[_TSelect]):
-        inherit_cache = True
-
-else:
-    from typing import GenericMeta  # type: ignore
-
-    class GenericSelectMeta(GenericMeta, _Select.__class__):  # type: ignore
-        pass
-
-    class _Py36Select(_Select, Generic[_TSelect], metaclass=GenericSelectMeta):
-        inherit_cache = True
-
-    class _Py36SelectOfScalar(_Select, Generic[_TSelect], metaclass=GenericSelectMeta):
-        inherit_cache = True
-
-    # Cast them for editors to work correctly, from several tricks tried, this works
-    # for both VS Code and PyCharm
-    Select = cast("Select", _Py36Select)  # type: ignore
-    SelectOfScalar = cast("SelectOfScalar", _Py36SelectOfScalar)  # type: ignore
+class Select(_Select, Generic[_TSelect]):
+    inherit_cache = True
 
+# This is not comparable to sqlalchemy.sql.selectable.ScalarSelect, that has a different
+# purpose. This is the same as a normal SQLAlchemy Select class where there's only one
+# entity, so the result will be converted to a scalar by default. This way writing
+# for loops on the results will feel natural.
+class SelectOfScalar(_Select, Generic[_TSelect]):
+    inherit_cache = True
 
 if TYPE_CHECKING:  # pragma: no cover
     from ..main import SQLModel