🗑️ Deprecate Python 3.6 and upgrade Poetry and Poetry Version Plugin (#627)

This commit is contained in:
Sebastián Ramírez 2023-07-29 12:32:47 +02:00 committed by GitHub
parent 43a689d369
commit 02bd7ebffd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 77 additions and 124 deletions

View File

@ -4,7 +4,9 @@ on:
branches: branches:
- main - main
pull_request: pull_request:
types: [opened, synchronize] types:
- opened
- synchronize
workflow_dispatch: workflow_dispatch:
inputs: inputs:
debug_enabled: debug_enabled:
@ -13,17 +15,17 @@ on:
default: false default: false
jobs: jobs:
build-docs: build-docs:
runs-on: ubuntu-20.04 runs-on: ubuntu-latest
steps: steps:
- name: Dump GitHub context - name: Dump GitHub context
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v3.1.0 - uses: actions/checkout@v3
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: "3.7" python-version: "3.11"
# Allow debugging with tmate # Allow debugging with tmate
- name: Setup tmate session - name: Setup tmate session
uses: mxschmitt/action-tmate@v3 uses: mxschmitt/action-tmate@v3
@ -34,34 +36,30 @@ jobs:
id: cache id: cache
with: with:
path: ${{ env.pythonLocation }} 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 - name: Install poetry
if: steps.cache.outputs.cache-hit != 'true' 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: | run: |
python -m pip install --upgrade pip 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"
python -m pip install "poetry==1.2.0a2" python -m poetry self add poetry-version-plugin
python -m poetry plugin add poetry-version-plugin
- name: Configure poetry - name: Configure poetry
run: python -m poetry config virtualenvs.create false run: python -m poetry config virtualenvs.create false
- name: Install Dependencies - name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true' if: steps.cache.outputs.cache-hit != 'true'
run: python -m poetry install run: python -m poetry install
- name: Install Material for MkDocs Insiders - name: Install Material for MkDocs Insiders
if: github.event.pull_request.head.repo.fork == false && steps.cache.outputs.cache-hit != 'true' 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.ACTIONS_TOKEN }}@github.com/squidfunk/mkdocs-material-insiders.git 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 - uses: actions/cache@v3
with: with:
key: mkdocs-cards-${{ github.ref }} key: mkdocs-cards-${{ github.ref }}
path: .cache path: .cache
- name: Build Docs - 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 run: python -m poetry run mkdocs build
- name: Build Docs with Insiders - 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 run: python -m poetry run mkdocs build --config-file mkdocs.insiders.yml
- name: Zip docs - name: Zip docs
run: python -m poetry run bash ./scripts/zip-docs.sh run: python -m poetry run bash ./scripts/zip-docs.sh
@ -70,7 +68,7 @@ jobs:
name: docs-zip name: docs-zip
path: ./site/docs.zip path: ./site/docs.zip
- name: Deploy to Netlify - name: Deploy to Netlify
uses: nwtgck/actions-netlify@v1.1.5 uses: nwtgck/actions-netlify@v2.0.0
with: with:
publish-dir: './site' publish-dir: './site'
production-branch: main production-branch: main

View File

@ -13,9 +13,9 @@ on:
jobs: jobs:
publish: publish:
runs-on: ubuntu-20.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3.1.0 - uses: actions/checkout@v3
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
@ -30,17 +30,13 @@ jobs:
id: cache id: cache
with: with:
path: ${{ env.pythonLocation }} 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 - name: Install poetry
if: steps.cache.outputs.cache-hit != 'true' 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: | run: |
python -m pip install --upgrade pip 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"
python -m pip install "poetry==1.2.0a2" python -m poetry self add poetry-version-plugin
python -m poetry plugin add poetry-version-plugin
- name: Configure poetry - name: Configure poetry
run: python -m poetry config virtualenvs.create false run: python -m poetry config virtualenvs.create false
- name: Install Dependencies - name: Install Dependencies

View File

@ -5,7 +5,9 @@ on:
branches: branches:
- main - main
pull_request: pull_request:
types: [opened, synchronize] types:
- opened
- synchronize
workflow_dispatch: workflow_dispatch:
inputs: inputs:
debug_enabled: debug_enabled:
@ -15,14 +17,18 @@ on:
jobs: jobs:
test: test:
runs-on: ubuntu-20.04 runs-on: ubuntu-latest
strategy: strategy:
matrix: 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 fail-fast: false
steps: steps:
- uses: actions/checkout@v3.1.0 - uses: actions/checkout@v3
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
@ -37,24 +43,19 @@ jobs:
id: cache id: cache
with: with:
path: ${{ env.pythonLocation }} 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 - name: Install poetry
if: steps.cache.outputs.cache-hit != 'true' 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: | run: |
python -m pip install --upgrade pip 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"
python -m pip install "poetry==1.2.0a2" python -m poetry self add poetry-version-plugin
python -m poetry plugin add poetry-version-plugin
- name: Configure poetry - name: Configure poetry
run: python -m poetry config virtualenvs.create false run: python -m poetry config virtualenvs.create false
- name: Install Dependencies - name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true' if: steps.cache.outputs.cache-hit != 'true'
run: python -m poetry install run: python -m poetry install
- name: Lint - name: Lint
if: ${{ matrix.python-version != '3.6.15' }}
run: python -m poetry run bash scripts/lint.sh run: python -m poetry run bash scripts/lint.sh
- run: mkdir coverage - run: mkdir coverage
- name: Test - name: Test
@ -68,7 +69,8 @@ jobs:
name: coverage name: coverage
path: coverage path: coverage
coverage-combine: coverage-combine:
needs: [test] needs:
- test
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -96,3 +98,15 @@ jobs:
with: with:
name: coverage-html name: coverage-html
path: htmlcov 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) }}

View File

@ -50,7 +50,7 @@ It combines SQLAlchemy and Pydantic and tries to simplify the code you write as
## Requirements ## 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. As **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.

View 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. 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 ### Poetry
**SQLModel** uses <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a> to build, package, and publish the project. **SQLModel** uses <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a> to build, package, and publish the project.

View File

@ -12,7 +12,7 @@ Nevertheless, SQLModel is completely **independent** of FastAPI and can be used
## Just Modern Python ## 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>. 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>.

View File

@ -50,7 +50,7 @@ It combines SQLAlchemy and Pydantic and tries to simplify the code you write as
## Requirements ## 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. As **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.

View File

@ -64,15 +64,13 @@ $ cd sqlmodel-tutorial
Make sure you have an officially supported version of Python. 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: You can check which version you have with:
<div class="termy"> <div class="termy">
```console ```console
$ python3 --version $ python3 --version
Python 3.6.9 Python 3.11
``` ```
</div> </div>
@ -84,8 +82,6 @@ You might want to try with the specific versions, for example with:
* `python3.10` * `python3.10`
* `python3.9` * `python3.9`
* `python3.8` * `python3.8`
* `python3.7`
* `python3.6`
The code would look like this: The code would look like this:

View File

@ -17,10 +17,10 @@ classifiers = [
"Intended Audience :: System Administrators", "Intended Audience :: System Administrators",
"License :: OSI Approved :: MIT License", "License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Topic :: Database", "Topic :: Database",
"Topic :: Database :: Database Engines/Servers", "Topic :: Database :: Database Engines/Servers",
"Topic :: Internet", "Topic :: Internet",
@ -30,7 +30,7 @@ classifiers = [
] ]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.6.1" python = "^3.7"
SQLAlchemy = ">=1.4.17,<=1.4.41" SQLAlchemy = ">=1.4.17,<=1.4.41"
pydantic = "^1.8.2" pydantic = "^1.8.2"
sqlalchemy2-stubs = {version = "*", allow-prereleases = true} sqlalchemy2-stubs = {version = "*", allow-prereleases = true}
@ -39,19 +39,17 @@ sqlalchemy2-stubs = {version = "*", allow-prereleases = true}
pytest = "^7.0.1" pytest = "^7.0.1"
mypy = "0.971" mypy = "0.971"
flake8 = "^5.0.4" flake8 = "^5.0.4"
black = {version = "^22.10.0", python = "^3.7"} black = "^22.10.0"
mkdocs = "^1.2.1" mkdocs = "^1.2.1"
mkdocs-material = "^8.1.4" mkdocs-material = "^8.1.4"
pillow = {version = "^9.3.0", python = "^3.7"} pillow = "^9.3.0"
cairosvg = {version = "^2.5.2", python = "^3.7"} cairosvg = "^2.5.2"
mdx-include = "^1.4.1" mdx-include = "^1.4.1"
coverage = {extras = ["toml"], version = "^6.2"} coverage = {extras = ["toml"], version = "^6.2"}
fastapi = "^0.68.1" fastapi = "^0.68.1"
requests = "^2.26.0" requests = "^2.26.0"
autoflake = "^1.4" autoflake = "^1.4"
isort = "^5.9.3" isort = "^5.9.3"
async_generator = {version = "*", python = "~3.6"}
async-exit-stack = {version = "*", python = "~3.6"}
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core"]

View File

@ -7,5 +7,3 @@ mypy sqlmodel
flake8 sqlmodel tests docs_src flake8 sqlmodel tests docs_src
black sqlmodel tests docs_src --check black sqlmodel tests docs_src --check
isort sqlmodel tests docs_src scripts --check-only 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

View File

@ -3,6 +3,7 @@
set -e set -e
set -x set -x
CHECK_JINJA=1 python scripts/generate_select.py
coverage run -m pytest tests coverage run -m pytest tests
coverage combine coverage combine
coverage report --show-missing coverage report --show-missing

View File

@ -11,6 +11,7 @@ from typing import (
Callable, Callable,
ClassVar, ClassVar,
Dict, Dict,
ForwardRef,
List, List,
Mapping, Mapping,
Optional, Optional,
@ -29,7 +30,7 @@ from pydantic.fields import SHAPE_SINGLETON
from pydantic.fields import FieldInfo as PydanticFieldInfo from pydantic.fields import FieldInfo as PydanticFieldInfo
from pydantic.fields import ModelField, Undefined, UndefinedType from pydantic.fields import ModelField, Undefined, UndefinedType
from pydantic.main import ModelMetaclass, validate_model 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 pydantic.utils import ROOT_KEY, Representation
from sqlalchemy import Boolean, Column, Date, DateTime from sqlalchemy import Boolean, Column, Date, DateTime
from sqlalchemy import Enum as sa_Enum from sqlalchemy import Enum as sa_Enum

View File

@ -1,6 +1,5 @@
# WARNING: do not modify this code, it is generated by expression.py.jinja2 # WARNING: do not modify this code, it is generated by expression.py.jinja2
import sys
from datetime import datetime from datetime import datetime
from typing import ( from typing import (
TYPE_CHECKING, TYPE_CHECKING,
@ -12,7 +11,6 @@ from typing import (
Type, Type,
TypeVar, TypeVar,
Union, Union,
cast,
overload, overload,
) )
from uuid import UUID from uuid import UUID
@ -24,37 +22,18 @@ from sqlalchemy.sql.expression import Select as _Select
_TSelect = TypeVar("_TSelect") _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]): class Select(_Select, Generic[_TSelect]):
inherit_cache = True 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 # This is not comparable to sqlalchemy.sql.selectable.ScalarSelect, that has a different
# entity, so the result will be converted to a scalar by default. This way writing # purpose. This is the same as a normal SQLAlchemy Select class where there's only one
# for loops on the results will feel natural. # entity, so the result will be converted to a scalar by default. This way writing
class SelectOfScalar(_Select, Generic[_TSelect]): # for loops on the results will feel natural.
class SelectOfScalar(_Select, Generic[_TSelect]):
inherit_cache = True 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
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
from ..main import SQLModel from ..main import SQLModel

View File

@ -1,4 +1,3 @@
import sys
from datetime import datetime from datetime import datetime
from typing import ( from typing import (
TYPE_CHECKING, TYPE_CHECKING,
@ -10,7 +9,6 @@ from typing import (
Type, Type,
TypeVar, TypeVar,
Union, Union,
cast,
overload, overload,
) )
from uuid import UUID from uuid import UUID
@ -22,38 +20,16 @@ from sqlalchemy.sql.expression import Select as _Select
_TSelect = TypeVar("_TSelect") _TSelect = TypeVar("_TSelect")
# Workaround Generics incompatibility in Python 3.6 class Select(_Select, Generic[_TSelect]):
# Ref: https://github.com/python/typing/issues/449#issuecomment-316061322
if sys.version_info.minor >= 7:
class Select(_Select, Generic[_TSelect]):
inherit_cache = True inherit_cache = True
# This is not comparable to sqlalchemy.sql.selectable.ScalarSelect, that has a different # 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 # 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 # entity, so the result will be converted to a scalar by default. This way writing
# for loops on the results will feel natural. # for loops on the results will feel natural.
class SelectOfScalar(_Select, Generic[_TSelect]): class SelectOfScalar(_Select, Generic[_TSelect]):
inherit_cache = True 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
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
from ..main import SQLModel from ..main import SQLModel