From: Federico Caselli Date: Sat, 26 Oct 2024 20:11:15 +0000 (+0200) Subject: Removed support for Python 3.8 since it's EOL. X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2adc79c07710c040ebb63019fc25674b4f876b26;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Removed support for Python 3.8 since it's EOL. Fixes: #12029 Change-Id: Ibb4efec9bab0225d03f6bf3fed661a3f2fc72cc7 --- diff --git a/.github/workflows/create-wheels.yaml b/.github/workflows/create-wheels.yaml index f9732bf09a..4c191d2678 100644 --- a/.github/workflows/create-wheels.yaml +++ b/.github/workflows/create-wheels.yaml @@ -20,7 +20,7 @@ jobs: matrix: # emulated wheels on linux take too much time, split wheels into multiple runs python: - - "cp38-* cp39-*" + - "cp39-*" - "cp310-* cp311-*" - "cp312-* cp313-*" wheel_mode: diff --git a/.github/workflows/run-test.yaml b/.github/workflows/run-test.yaml index 5e2b696e3e..133997b5d3 100644 --- a/.github/workflows/run-test.yaml +++ b/.github/workflows/run-test.yaml @@ -31,12 +31,11 @@ jobs: - "macos-latest" - "macos-13" python-version: - - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" - - "3.13.0-alpha - 3.13" + - "3.13" - "pypy-3.10" build-type: - "cext" @@ -68,8 +67,6 @@ jobs: architecture: x86 - os: "macos-latest" architecture: x64 - - os: "macos-latest" - python-version: "3.8" - os: "macos-latest" python-version: "3.9" # macos 13: uses intel macs. no arm64, x86 @@ -120,7 +117,6 @@ jobs: strategy: matrix: python-version: - - cp38-cp38 - cp39-cp39 - cp310-cp310 - cp311-cp311 @@ -162,12 +158,11 @@ jobs: os: - "ubuntu-latest" python-version: - - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" - - "3.13.0-alpha - 3.13" + - "3.13" tox-env: - mypy - pep484 @@ -179,8 +174,6 @@ jobs: os: "ubuntu-latest" exclude: # run pep484 only on 3.10+ - - tox-env: pep484 - python-version: "3.8" - tox-env: pep484 python-version: "3.9" diff --git a/doc/build/changelog/unreleased_21/10357.rst b/doc/build/changelog/unreleased_21/10357.rst index 37fa158f67..22772678fa 100644 --- a/doc/build/changelog/unreleased_21/10357.rst +++ b/doc/build/changelog/unreleased_21/10357.rst @@ -1,6 +1,6 @@ .. change:: :tags: change, installation - :tickets: 10357 + :tickets: 10357, 12029 - Python 3.8 or above is now required; support for Python 3.7 is dropped as - this version is EOL. + Python 3.9 or above is now required; support for Python 3.8 and 3.7 is + dropped as these versions are EOL. diff --git a/doc/build/intro.rst b/doc/build/intro.rst index ee93cc3295..cba95ab69e 100644 --- a/doc/build/intro.rst +++ b/doc/build/intro.rst @@ -96,11 +96,11 @@ Supported Platforms SQLAlchemy 2.1 supports the following platforms: -* cPython 3.8 and higher +* cPython 3.9 and higher * Python-3 compatible versions of `PyPy `_ .. versionchanged:: 2.1 - SQLAlchemy now targets Python 3.8 and above. + SQLAlchemy now targets Python 3.9 and above. Supported Installation Methods diff --git a/doc/build/orm/collection_api.rst b/doc/build/orm/collection_api.rst index 07e4a4ce88..2d490d7e55 100644 --- a/doc/build/orm/collection_api.rst +++ b/doc/build/orm/collection_api.rst @@ -47,7 +47,7 @@ below where ``list`` is used:: parent_id: Mapped[int] = mapped_column(primary_key=True) # use a list - children: Mapped[List["Child"]] = relationship() + children: Mapped[list["Child"]] = relationship() class Child(Base): @@ -59,7 +59,6 @@ below where ``list`` is used:: Or for a ``set``, illustrated in the same ``Parent.children`` collection:: - from typing import Set from sqlalchemy import ForeignKey from sqlalchemy.orm import DeclarativeBase @@ -78,7 +77,7 @@ Or for a ``set``, illustrated in the same parent_id: Mapped[int] = mapped_column(primary_key=True) # use a set - children: Mapped[Set["Child"]] = relationship() + children: Mapped[set["Child"]] = relationship() class Child(Base): @@ -87,22 +86,6 @@ Or for a ``set``, illustrated in the same child_id: Mapped[int] = mapped_column(primary_key=True) parent_id: Mapped[int] = mapped_column(ForeignKey("parent.id")) -.. note:: If using Python 3.8, annotations for collections need - to use ``typing.List`` or ``typing.Set``, e.g. ``Mapped[List["Child"]]`` or - ``Mapped[Set["Child"]]``; the ``list`` and ``set`` Python built-ins - don't yet support generic annotation in these Python versions, such as:: - - from typing import List - - - class Parent(Base): - __tablename__ = "parent" - - parent_id: Mapped[int] = mapped_column(primary_key=True) - - # use a List, Python 3.8 and earlier - children: Mapped[List["Child"]] = relationship() - When using mappings without the :class:`_orm.Mapped` annotation, such as when using :ref:`imperative mappings ` or untyped Python code, as well as in a few special cases, the collection class for a diff --git a/lib/sqlalchemy/engine/cursor.py b/lib/sqlalchemy/engine/cursor.py index 8a2a47cb89..491ef9e443 100644 --- a/lib/sqlalchemy/engine/cursor.py +++ b/lib/sqlalchemy/engine/cursor.py @@ -49,7 +49,6 @@ from ..sql.compiler import RM_OBJECTS from ..sql.compiler import RM_RENDERED_NAME from ..sql.compiler import RM_TYPE from ..sql.type_api import TypeEngine -from ..util import compat from ..util.typing import Literal from ..util.typing import Self from ..util.typing import TupleAny @@ -325,16 +324,14 @@ class CursorResultMetaData(ResultMetaData): assert not self._tuplefilter return self._make_new_metadata( - keymap=compat.dict_union( - self._keymap, - { - new: keymap_by_position[idx] - for idx, new in enumerate( - invoked_statement._all_selected_columns - ) - if idx in keymap_by_position - }, - ), + keymap=self._keymap + | { + new: keymap_by_position[idx] + for idx, new in enumerate( + invoked_statement._all_selected_columns + ) + if idx in keymap_by_position + }, unpickled=self._unpickled, processors=self._processors, tuplefilter=None, diff --git a/lib/sqlalchemy/testing/fixtures/mypy.py b/lib/sqlalchemy/testing/fixtures/mypy.py index 149df9f7d4..5a167d2b40 100644 --- a/lib/sqlalchemy/testing/fixtures/mypy.py +++ b/lib/sqlalchemy/testing/fixtures/mypy.py @@ -203,22 +203,6 @@ class MypyTest(TestBase): is_mypy = is_re = True expected_msg = f'Revealed type is "{expected_msg}"' - if mypy_14 and util.py39: - # use_lowercase_names, py39 and above - # https://github.com/python/mypy/blob/304997bfb85200fb521ac727ee0ce3e6085e5278/mypy/options.py#L363 # noqa: E501 - - # skip first character which could be capitalized - # "List item x not found" type of message - expected_msg = expected_msg[0] + re.sub( - ( - r"\b(List|Tuple|Dict|Set)\b" - if is_type - else r"\b(List|Tuple|Dict|Set|Type)\b" - ), - lambda m: m.group(1).lower(), - expected_msg[1:], - ) - if mypy_14 and util.py310: # use_or_syntax, py310 and above # https://github.com/python/mypy/blob/304997bfb85200fb521ac727ee0ce3e6085e5278/mypy/options.py#L368 # noqa: E501 diff --git a/lib/sqlalchemy/testing/requirements.py b/lib/sqlalchemy/testing/requirements.py index 544f87ec99..b1d3d0f085 100644 --- a/lib/sqlalchemy/testing/requirements.py +++ b/lib/sqlalchemy/testing/requirements.py @@ -1516,12 +1516,6 @@ class SuiteRequirements(Requirements): return exclusions.skip_if(check) - @property - def python39(self): - return exclusions.only_if( - lambda: util.py39, "Python 3.9 or above required" - ) - @property def python310(self): return exclusions.only_if( diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py index ca3d6b8b55..16c109c0bb 100644 --- a/lib/sqlalchemy/util/__init__.py +++ b/lib/sqlalchemy/util/__init__.py @@ -66,7 +66,6 @@ from .compat import py310 as py310 from .compat import py311 as py311 from .compat import py312 as py312 from .compat import py313 as py313 -from .compat import py39 as py39 from .compat import pypy as pypy from .compat import win32 as win32 from .concurrency import await_ as await_ diff --git a/lib/sqlalchemy/util/compat.py b/lib/sqlalchemy/util/compat.py index 01643e05c3..e7511c94fc 100644 --- a/lib/sqlalchemy/util/compat.py +++ b/lib/sqlalchemy/util/compat.py @@ -35,7 +35,6 @@ py313 = sys.version_info >= (3, 13) py312 = sys.version_info >= (3, 12) py311 = sys.version_info >= (3, 11) py310 = sys.version_info >= (3, 10) -py39 = sys.version_info >= (3, 9) pypy = platform.python_implementation() == "PyPy" cpython = platform.python_implementation() == "CPython" @@ -97,27 +96,10 @@ def inspect_getfullargspec(func: Callable[..., Any]) -> FullArgSpec: ) -if py39: - # python stubs don't have a public type for this. not worth - # making a protocol - def md5_not_for_security() -> Any: - return hashlib.md5(usedforsecurity=False) - -else: - - def md5_not_for_security() -> Any: - return hashlib.md5() - - -if typing.TYPE_CHECKING or py39: - # pep 584 dict union - dict_union = operator.or_ # noqa -else: - - def dict_union(a: dict, b: dict) -> dict: - a = a.copy() - a.update(b) - return a +# python stubs don't have a public type for this. not worth +# making a protocol +def md5_not_for_security() -> Any: + return hashlib.md5(usedforsecurity=False) if py310: diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py index 632e6a0a56..82cfca8c55 100644 --- a/lib/sqlalchemy/util/langhelpers.py +++ b/lib/sqlalchemy/util/langhelpers.py @@ -66,15 +66,11 @@ if compat.py310: else: def get_annotations(obj: Any) -> Mapping[str, Any]: - # it's been observed that cls.__annotations__ can be non present. - # it's not clear what causes this, running under tox py38 it - # happens, running straight pytest it doesnt - # https://docs.python.org/3/howto/annotations.html#annotations-howto if isinstance(obj, type): ann = obj.__dict__.get("__annotations__", None) else: - ann = getattr(obj, "__annotations__", None) + ann = obj.__annotations__ if ann is None: return _collections.EMPTY_DICT diff --git a/lib/sqlalchemy/util/typing.py b/lib/sqlalchemy/util/typing.py index 3366fca499..7510e7a387 100644 --- a/lib/sqlalchemy/util/typing.py +++ b/lib/sqlalchemy/util/typing.py @@ -368,8 +368,7 @@ def is_literal(type_: _AnnotationScanType) -> bool: def is_newtype(type_: Optional[_AnnotationScanType]) -> TypeGuard[NewType]: return hasattr(type_, "__supertype__") - - # doesn't work in 3.8, 3.7 as it passes a closure, not an + # doesn't work in 3.9, 3.8, 3.7 as it passes a closure, not an # object instance # return isinstance(type_, NewType) diff --git a/pyproject.toml b/pyproject.toml index 38867508db..eebbd725bc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,6 @@ classifiers = [ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -29,7 +28,7 @@ classifiers = [ "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Database :: Front-Ends", ] -requires-python = ">=3.8" +requires-python = ">=3.9" dependencies = [ "typing-extensions >= 4.6.0", ] @@ -118,7 +117,7 @@ tag-build = "dev" [tool.black] line-length = 79 -target-version = ['py38'] +target-version = ['py39'] [tool.zimports] diff --git a/test/base/test_utils.py b/test/base/test_utils.py index 0ca60c7931..77ab9ff222 100644 --- a/test/base/test_utils.py +++ b/test/base/test_utils.py @@ -470,7 +470,6 @@ class ImmutableDictTest(fixtures.TestBase): i2 = util.immutabledict({"a": 42, 42: "a"}) eq_(str(i2), "immutabledict({'a': 42, 42: 'a'})") - @testing.requires.python39 def test_pep584(self): i = util.immutabledict({"a": 2}) with expect_raises_message(TypeError, "object is immutable"): @@ -3644,9 +3643,12 @@ class CyExtensionTest(fixtures.TestBase): import setuptools # noqa: F401 except ImportError: testing.skip_test("setuptools is required") - with mock.patch("setuptools.setup", mock.MagicMock()), mock.patch.dict( - "os.environ", - {"DISABLE_SQLALCHEMY_CEXT": "", "REQUIRE_SQLALCHEMY_CEXT": ""}, + with ( + mock.patch("setuptools.setup", mock.MagicMock()), + mock.patch.dict( + "os.environ", + {"DISABLE_SQLALCHEMY_CEXT": "", "REQUIRE_SQLALCHEMY_CEXT": ""}, + ), ): import setup diff --git a/test/dialect/mysql/test_dialect.py b/test/dialect/mysql/test_dialect.py index cf74f17ad6..23dbd39957 100644 --- a/test/dialect/mysql/test_dialect.py +++ b/test/dialect/mysql/test_dialect.py @@ -40,18 +40,22 @@ class BackendDialectTest( """ engine = testing_engine() _server_version = [None] - with mock.patch.object( - engine.dialect, - "_get_server_version_info", - lambda conn: engine.dialect._parse_server_version( - _server_version[0] + with ( + mock.patch.object( + engine.dialect, + "_get_server_version_info", + lambda conn: engine.dialect._parse_server_version( + _server_version[0] + ), + ), + mock.patch.object( + engine.dialect, "_set_mariadb", lambda *arg: None + ), + mock.patch.object( + engine.dialect, + "get_isolation_level", + lambda *arg: "REPEATABLE READ", ), - ), mock.patch.object( - engine.dialect, "_set_mariadb", lambda *arg: None - ), mock.patch.object( - engine.dialect, - "get_isolation_level", - lambda *arg: "REPEATABLE READ", ): def go(server_version): diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py index 148d0be1a2..df70bac14f 100644 --- a/test/engine/test_execute.py +++ b/test/engine/test_execute.py @@ -427,20 +427,24 @@ class ExecuteTest(fixtures.TablesTest): # TODO: this test is assuming too much of arbitrary dialects and would # be better suited tested against a single mock dialect that does not # have any special behaviors - with patch.object( - testing.db.dialect, "dbapi", Mock(Error=DBAPIError) - ), patch.object( - testing.db.dialect, "loaded_dbapi", Mock(Error=DBAPIError) - ), patch.object( - testing.db.dialect, "is_disconnect", lambda *arg: False - ), patch.object( - testing.db.dialect, - "do_execute", - Mock(side_effect=NonStandardException), - ), patch.object( - testing.db.dialect.execution_ctx_cls, - "handle_dbapi_exception", - Mock(), + with ( + patch.object(testing.db.dialect, "dbapi", Mock(Error=DBAPIError)), + patch.object( + testing.db.dialect, "loaded_dbapi", Mock(Error=DBAPIError) + ), + patch.object( + testing.db.dialect, "is_disconnect", lambda *arg: False + ), + patch.object( + testing.db.dialect, + "do_execute", + Mock(side_effect=NonStandardException), + ), + patch.object( + testing.db.dialect.execution_ctx_cls, + "handle_dbapi_exception", + Mock(), + ), ): with testing.db.connect() as conn: assert_raises( @@ -1001,11 +1005,14 @@ class ConvenienceExecuteTest(fixtures.TablesTest): engine = engines.testing_engine() close_mock = Mock() - with mock.patch.object( - engine._connection_cls, - "begin", - Mock(side_effect=Exception("boom")), - ), mock.patch.object(engine._connection_cls, "close", close_mock): + with ( + mock.patch.object( + engine._connection_cls, + "begin", + Mock(side_effect=Exception("boom")), + ), + mock.patch.object(engine._connection_cls, "close", close_mock), + ): with expect_raises_message(Exception, "boom"): with engine.begin(): pass @@ -1894,11 +1901,12 @@ class EngineEventsTest(fixtures.TestBase): # as part of create # note we can't use an event to ensure begin() is not called # because create also blocks events from happening - with mock.patch.object( - e1.dialect, "initialize", side_effect=init - ) as m1, mock.patch.object( - e1._connection_cls, "begin" - ) as begin_mock: + with ( + mock.patch.object( + e1.dialect, "initialize", side_effect=init + ) as m1, + mock.patch.object(e1._connection_cls, "begin") as begin_mock, + ): @event.listens_for(e1, "connect", insert=True) def go1(dbapi_conn, xyz): @@ -2536,11 +2544,14 @@ class EngineEventsTest(fixtures.TestBase): def conn_tracker(conn, opt): opt["conn_tracked"] = True - with mock.patch.object( - engine.dialect, "set_connection_execution_options" - ) as conn_opt, mock.patch.object( - engine.dialect, "set_engine_execution_options" - ) as engine_opt: + with ( + mock.patch.object( + engine.dialect, "set_connection_execution_options" + ) as conn_opt, + mock.patch.object( + engine.dialect, "set_engine_execution_options" + ) as engine_opt, + ): e2 = engine.execution_options(e1="opt_e1") c1 = engine.connect() c2 = c1.execution_options(c1="opt_c1") @@ -3493,11 +3504,12 @@ class OnConnectTest(fixtures.TestBase): nonlocal init_connection init_connection = connection - with mock.patch.object( - e._connection_cls, "begin" - ) as mock_begin, mock.patch.object( - e.dialect, "initialize", Mock(side_effect=mock_initialize) - ) as mock_init: + with ( + mock.patch.object(e._connection_cls, "begin") as mock_begin, + mock.patch.object( + e.dialect, "initialize", Mock(side_effect=mock_initialize) + ) as mock_init, + ): conn = e.connect() eq_(mock_begin.mock_calls, []) @@ -3928,12 +3940,16 @@ class SetInputSizesTest(fixtures.TablesTest): # "safe" datatypes so that the DBAPI does not actually need # setinputsizes() called in order to work. - with mock.patch.object( - engine.dialect, "bind_typing", BindTyping.SETINPUTSIZES - ), mock.patch.object( - engine.dialect, "do_set_input_sizes", do_set_input_sizes - ), mock.patch.object( - engine.dialect.execution_ctx_cls, "pre_exec", pre_exec + with ( + mock.patch.object( + engine.dialect, "bind_typing", BindTyping.SETINPUTSIZES + ), + mock.patch.object( + engine.dialect, "do_set_input_sizes", do_set_input_sizes + ), + mock.patch.object( + engine.dialect.execution_ctx_cls, "pre_exec", pre_exec + ), ): yield engine, canary diff --git a/test/engine/test_transaction.py b/test/engine/test_transaction.py index fb67c7434f..182d680f0c 100644 --- a/test/engine/test_transaction.py +++ b/test/engine/test_transaction.py @@ -1263,12 +1263,13 @@ class IsolationLevelTest(fixtures.TestBase): def test_underscore_replacement(self, connection_no_trans): conn = connection_no_trans - with mock.patch.object( - conn.dialect, "set_isolation_level" - ) as mock_sil, mock.patch.object( - conn.dialect, - "_gen_allowed_isolation_levels", - mock.Mock(return_value=["READ COMMITTED", "REPEATABLE READ"]), + with ( + mock.patch.object(conn.dialect, "set_isolation_level") as mock_sil, + mock.patch.object( + conn.dialect, + "_gen_allowed_isolation_levels", + mock.Mock(return_value=["READ COMMITTED", "REPEATABLE READ"]), + ), ): conn.execution_options(isolation_level="REPEATABLE_READ") dbapi_conn = conn.connection.dbapi_connection @@ -1277,12 +1278,13 @@ class IsolationLevelTest(fixtures.TestBase): def test_casing_replacement(self, connection_no_trans): conn = connection_no_trans - with mock.patch.object( - conn.dialect, "set_isolation_level" - ) as mock_sil, mock.patch.object( - conn.dialect, - "_gen_allowed_isolation_levels", - mock.Mock(return_value=["READ COMMITTED", "REPEATABLE READ"]), + with ( + mock.patch.object(conn.dialect, "set_isolation_level") as mock_sil, + mock.patch.object( + conn.dialect, + "_gen_allowed_isolation_levels", + mock.Mock(return_value=["READ COMMITTED", "REPEATABLE READ"]), + ), ): conn.execution_options(isolation_level="repeatable_read") dbapi_conn = conn.connection.dbapi_connection @@ -1645,9 +1647,12 @@ class ResetFixture: event.listen(engine, "rollback_twophase", harness.rollback_twophase) event.listen(engine, "commit_twophase", harness.commit_twophase) - with mock.patch.object( - engine.dialect, "do_rollback", harness.do_rollback - ), mock.patch.object(engine.dialect, "do_commit", harness.do_commit): + with ( + mock.patch.object( + engine.dialect, "do_rollback", harness.do_rollback + ), + mock.patch.object(engine.dialect, "do_commit", harness.do_commit), + ): yield harness event.remove(engine, "rollback", harness.rollback) diff --git a/test/ext/asyncio/test_engine_py3k.py b/test/ext/asyncio/test_engine_py3k.py index 60edbf608d..a37b088c7d 100644 --- a/test/ext/asyncio/test_engine_py3k.py +++ b/test/ext/asyncio/test_engine_py3k.py @@ -372,11 +372,14 @@ class AsyncEngineTest(EngineFixture): # the thing here that emits the warning is the correct path from sqlalchemy.pool.base import _finalize_fairy - with mock.patch.object( - pool._dialect, - "do_rollback", - mock.Mock(side_effect=Exception("can't run rollback")), - ), mock.patch("sqlalchemy.util.warn") as m: + with ( + mock.patch.object( + pool._dialect, + "do_rollback", + mock.Mock(side_effect=Exception("can't run rollback")), + ), + mock.patch("sqlalchemy.util.warn") as m, + ): _finalize_fairy( None, rec, pool, ref, echo, transaction_was_reset=False ) diff --git a/test/ext/mypy/plugin_files/mixin_not_mapped.py b/test/ext/mypy/plugin_files/mixin_not_mapped.py index 9a4865eb6d..e9aa336c8d 100644 --- a/test/ext/mypy/plugin_files/mixin_not_mapped.py +++ b/test/ext/mypy/plugin_files/mixin_not_mapped.py @@ -33,9 +33,9 @@ class Bar(HasUpdatedAt, Base): Bar.__mapper__ -# EXPECTED_MYPY: "Type[HasUpdatedAt]" has no attribute "__mapper__" +# EXPECTED_MYPY: "type[HasUpdatedAt]" has no attribute "__mapper__" HasUpdatedAt.__mapper__ -# EXPECTED_MYPY: "Type[SomeAbstract]" has no attribute "__mapper__" +# EXPECTED_MYPY: "type[SomeAbstract]" has no attribute "__mapper__" SomeAbstract.__mapper__ diff --git a/test/ext/mypy/plugin_files/orderinglist1.py b/test/ext/mypy/plugin_files/orderinglist1.py index 661d55a7b6..fb05b767a5 100644 --- a/test/ext/mypy/plugin_files/orderinglist1.py +++ b/test/ext/mypy/plugin_files/orderinglist1.py @@ -21,5 +21,5 @@ class A: a1 = A(id=5, ordering=10) -# EXPECTED_MYPY: Argument "parents" to "A" has incompatible type "List[A]"; expected "Mapped[Any]" # noqa +# EXPECTED_MYPY: Argument "parents" to "A" has incompatible type "list[A]"; expected "Mapped[Any]" # noqa a2 = A(parents=[a1]) diff --git a/test/ext/mypy/plugin_files/orderinglist2.py b/test/ext/mypy/plugin_files/orderinglist2.py index eb50c5391b..d8b179e9a7 100644 --- a/test/ext/mypy/plugin_files/orderinglist2.py +++ b/test/ext/mypy/plugin_files/orderinglist2.py @@ -37,10 +37,10 @@ class A: B, collection_class=ordering_list("ordering") ) - # EXPECTED: Left hand assignment 'cs: "List[B]"' not compatible with ORM mapped expression of type "Mapped[List[C]]" # noqa + # EXPECTED: Left hand assignment 'cs: "list[B]"' not compatible with ORM mapped expression of type "Mapped[list[C]]" # noqa cs: List[B] = relationship(C, uselist=True) - # EXPECTED: Left hand assignment 'cs_2: "B"' not compatible with ORM mapped expression of type "Mapped[List[C]]" # noqa + # EXPECTED: Left hand assignment 'cs_2: "B"' not compatible with ORM mapped expression of type "Mapped[list[C]]" # noqa cs_2: B = relationship(C, uselist=True) diff --git a/test/ext/mypy/plugin_files/relationship_err2.py b/test/ext/mypy/plugin_files/relationship_err2.py index 4057baeb37..04db946abf 100644 --- a/test/ext/mypy/plugin_files/relationship_err2.py +++ b/test/ext/mypy/plugin_files/relationship_err2.py @@ -28,5 +28,5 @@ class A(Base): # EXPECTED_MYPY: List item 1 has incompatible type "A"; expected "B" a1 = A(bs=[B(data="b"), A()]) -# EXPECTED_MYPY: Incompatible types in assignment (expression has type "List[B]", variable has type "Set[B]") # noqa +# EXPECTED_MYPY: Incompatible types in assignment (expression has type "list[B]", variable has type "set[B]") # noqa x: Set[B] = a1.bs diff --git a/test/ext/mypy/plugin_files/relationship_err3.py b/test/ext/mypy/plugin_files/relationship_err3.py index 1c7cd9f303..95d77fde59 100644 --- a/test/ext/mypy/plugin_files/relationship_err3.py +++ b/test/ext/mypy/plugin_files/relationship_err3.py @@ -27,7 +27,7 @@ class A(Base): bs: Set[B] = relationship(B, uselist=True, back_populates="a") - # EXPECTED: Left hand assignment 'another_bs: "Set[B]"' not compatible with ORM mapped expression of type "Mapped[B]" # noqa + # EXPECTED: Left hand assignment 'another_bs: "set[B]"' not compatible with ORM mapped expression of type "Mapped[B]" # noqa another_bs: Set[B] = relationship(B, viewonly=True) diff --git a/test/orm/declarative/test_basic.py b/test/orm/declarative/test_basic.py index 192c46aff2..c80e8cd263 100644 --- a/test/orm/declarative/test_basic.py +++ b/test/orm/declarative/test_basic.py @@ -1577,11 +1577,14 @@ class DeclarativeMultiBaseTest( attr_type.fail() def test_column_named_twice(self): - with expect_warnings( - "On class 'Foo', Column object 'x' named directly multiple " - "times, only one will be used: x, y. Consider using " - "orm.synonym instead" - ), expect_raises(exc.DuplicateColumnError): + with ( + expect_warnings( + "On class 'Foo', Column object 'x' named directly multiple " + "times, only one will be used: x, y. Consider using " + "orm.synonym instead" + ), + expect_raises(exc.DuplicateColumnError), + ): class Foo(Base): __tablename__ = "foo" @@ -1592,11 +1595,14 @@ class DeclarativeMultiBaseTest( @testing.variation("style", ["old", "new"]) def test_column_repeated_under_prop(self, style): - with expect_warnings( - "On class 'Foo', Column object 'x' named directly multiple " - "times, only one will be used: x, y, z. Consider using " - "orm.synonym instead" - ), expect_raises(exc.DuplicateColumnError): + with ( + expect_warnings( + "On class 'Foo', Column object 'x' named directly multiple " + "times, only one will be used: x, y, z. Consider using " + "orm.synonym instead" + ), + expect_raises(exc.DuplicateColumnError), + ): if style.old: class Foo(Base): diff --git a/test/orm/declarative/test_dc_transforms.py b/test/orm/declarative/test_dc_transforms.py index 52c4dae51a..51a74d5afc 100644 --- a/test/orm/declarative/test_dc_transforms.py +++ b/test/orm/declarative/test_dc_transforms.py @@ -226,9 +226,12 @@ class DCTransformsTest(AssertsCompiledSQL, fixtures.TestBase): foo: Mapped[str] bar: Mapped[str] = mapped_column() - with _dataclass_mixin_warning( - "_BaseMixin", "'create_user', 'update_user'" - ), _dataclass_mixin_warning("SubMixin", "'foo', 'bar'"): + with ( + _dataclass_mixin_warning( + "_BaseMixin", "'create_user', 'update_user'" + ), + _dataclass_mixin_warning("SubMixin", "'foo', 'bar'"), + ): class User(SubMixin, Base): __tablename__ = "sys_user" diff --git a/test/orm/inheritance/test_relationship.py b/test/orm/inheritance/test_relationship.py index be42dc6090..e2016f8b5d 100644 --- a/test/orm/inheritance/test_relationship.py +++ b/test/orm/inheritance/test_relationship.py @@ -2712,8 +2712,9 @@ class MultipleAdaptUsesEntityOverTableTest( def test_two_joins_adaption(self): a, c, d = self.tables.a, self.tables.c, self.tables.d - with _aliased_join_warning(r"C\(c\)"), _aliased_join_warning( - r"D\(d\)" + with ( + _aliased_join_warning(r"C\(c\)"), + _aliased_join_warning(r"D\(d\)"), ): q = self._two_join_fixture()._compile_state() @@ -2745,8 +2746,9 @@ class MultipleAdaptUsesEntityOverTableTest( def test_two_joins_sql(self): q = self._two_join_fixture() - with _aliased_join_warning(r"C\(c\)"), _aliased_join_warning( - r"D\(d\)" + with ( + _aliased_join_warning(r"C\(c\)"), + _aliased_join_warning(r"D\(d\)"), ): self.assert_compile( q, diff --git a/test/orm/test_bind.py b/test/orm/test_bind.py index abd008cadf..317ebdc468 100644 --- a/test/orm/test_bind.py +++ b/test/orm/test_bind.py @@ -463,16 +463,22 @@ class BindIntegrationTest(_fixtures.FixtureTest): engine = {"e1": e1, "e2": e2, "e3": e3}[expected_engine_name] - with mock.patch( - "sqlalchemy.orm.context.ORMCompileState.orm_setup_cursor_result" - ), mock.patch( - "sqlalchemy.orm.context.ORMCompileState.orm_execute_statement" - ), mock.patch( - "sqlalchemy.orm.bulk_persistence." - "BulkORMInsert.orm_execute_statement" - ), mock.patch( - "sqlalchemy.orm.bulk_persistence." - "BulkUDCompileState.orm_setup_cursor_result" + with ( + mock.patch( + "sqlalchemy.orm.context.ORMCompileState." + "orm_setup_cursor_result" + ), + mock.patch( + "sqlalchemy.orm.context.ORMCompileState.orm_execute_statement" + ), + mock.patch( + "sqlalchemy.orm.bulk_persistence." + "BulkORMInsert.orm_execute_statement" + ), + mock.patch( + "sqlalchemy.orm.bulk_persistence." + "BulkUDCompileState.orm_setup_cursor_result" + ), ): sess.execute(statement) diff --git a/test/orm/test_events.py b/test/orm/test_events.py index 5e1672b526..287f436464 100644 --- a/test/orm/test_events.py +++ b/test/orm/test_events.py @@ -2580,8 +2580,9 @@ class SessionEventsTest(RemoveORMEventsGlobally, _fixtures.FixtureTest): u2 = User(name="u1", id=1) sess.add(u2) - with expect_raises(sa.exc.IntegrityError), expect_warnings( - "New instance" + with ( + expect_raises(sa.exc.IntegrityError), + expect_warnings("New instance"), ): sess.commit() @@ -2636,8 +2637,9 @@ class SessionEventsTest(RemoveORMEventsGlobally, _fixtures.FixtureTest): u2 = User(name="u1", id=1) sess.add(u2) - with expect_raises(sa.exc.IntegrityError), expect_warnings( - "New instance" + with ( + expect_raises(sa.exc.IntegrityError), + expect_warnings("New instance"), ): sess.commit() diff --git a/test/orm/test_transaction.py b/test/orm/test_transaction.py index 67b6042361..2f7a2f1980 100644 --- a/test/orm/test_transaction.py +++ b/test/orm/test_transaction.py @@ -671,13 +671,16 @@ class SessionTransactionTest(fixtures.RemovesEvents, FixtureTest): def fail(*arg, **kw): raise BaseException("some base exception") - with mock.patch.object( - testing.db.dialect, "do_rollback", side_effect=fail - ) as fail_mock, mock.patch.object( - testing.db.dialect, - "do_commit", - side_effect=testing.db.dialect.do_commit, - ) as succeed_mock: + with ( + mock.patch.object( + testing.db.dialect, "do_rollback", side_effect=fail + ) as fail_mock, + mock.patch.object( + testing.db.dialect, + "do_commit", + side_effect=testing.db.dialect.do_commit, + ) as succeed_mock, + ): # sess.begin() -> commit(). why would do_rollback() be called? # because of connection pool finalize_fairy *after* the commit. # this will cause the conn.close() in session.commit() to fail, diff --git a/test/orm/test_versioning.py b/test/orm/test_versioning.py index 1cf3140a56..46821fe055 100644 --- a/test/orm/test_versioning.py +++ b/test/orm/test_versioning.py @@ -429,9 +429,12 @@ class VersioningTest(fixtures.MappedTest): else: return self.context.rowcount - with patch.object( - config.db.dialect, "supports_sane_multi_rowcount", False - ), patch("sqlalchemy.engine.cursor.CursorResult.rowcount", rowcount): + with ( + patch.object( + config.db.dialect, "supports_sane_multi_rowcount", False + ), + patch("sqlalchemy.engine.cursor.CursorResult.rowcount", rowcount), + ): Foo = self.classes.Foo s1 = self._fixture() f1s1 = Foo(value="f1 value") @@ -444,10 +447,11 @@ class VersioningTest(fixtures.MappedTest): eq_(f1s1.version_id, 2) def test_update_delete_no_plain_rowcount(self): - with patch.object( - config.db.dialect, "supports_sane_rowcount", False - ), patch.object( - config.db.dialect, "supports_sane_multi_rowcount", False + with ( + patch.object(config.db.dialect, "supports_sane_rowcount", False), + patch.object( + config.db.dialect, "supports_sane_multi_rowcount", False + ), ): Foo = self.classes.Foo s1 = self._fixture() @@ -714,10 +718,11 @@ class VersionOnPostUpdateTest(fixtures.MappedTest): n1.related.append(n2) - with patch.object( - config.db.dialect, "supports_sane_rowcount", False - ), patch.object( - config.db.dialect, "supports_sane_multi_rowcount", False + with ( + patch.object(config.db.dialect, "supports_sane_rowcount", False), + patch.object( + config.db.dialect, "supports_sane_multi_rowcount", False + ), ): s2 = Session(bind=s.connection(bind_arguments=dict(mapper=Node))) s2.query(Node).filter(Node.id == n2.id).update({"version_id": 3}) diff --git a/test/sql/test_resultset.py b/test/sql/test_resultset.py index 26de957e1e..f87c6520d9 100644 --- a/test/sql/test_resultset.py +++ b/test/sql/test_resultset.py @@ -3582,9 +3582,10 @@ class AlternateCursorResultTest(fixtures.TablesTest): r = conn.execute(select(self.table).limit(1)) r.fetchone() - with mock.patch.object( - r, "_soft_close", raise_ - ), testing.expect_raises_message(IOError, "random non-DBAPI"): + with ( + mock.patch.object(r, "_soft_close", raise_), + testing.expect_raises_message(IOError, "random non-DBAPI"), + ): r.first() r.close() diff --git a/test/typing/plain_files/dialects/postgresql/pg_stuff.py b/test/typing/plain_files/dialects/postgresql/pg_stuff.py index a25a0b8cce..8d74ba03e8 100644 --- a/test/typing/plain_files/dialects/postgresql/pg_stuff.py +++ b/test/typing/plain_files/dialects/postgresql/pg_stuff.py @@ -68,7 +68,7 @@ print(stmt) t1 = Test() -# EXPECTED_RE_TYPE: .*[dD]ict\[.*str, Any\] +# EXPECTED_RE_TYPE: .*dict\[.*str, Any\] reveal_type(t1.data) # EXPECTED_TYPE: UUID diff --git a/test/typing/plain_files/orm/session.py b/test/typing/plain_files/orm/session.py index 39b41dfbb7..1cc5b1c014 100644 --- a/test/typing/plain_files/orm/session.py +++ b/test/typing/plain_files/orm/session.py @@ -55,13 +55,13 @@ with Session(e) as sess: rows1 = q.all() - # EXPECTED_RE_TYPE: builtins.[Ll]ist\[.*User\*?\] + # EXPECTED_RE_TYPE: builtins.list\[.*User\*?\] reveal_type(rows1) q2 = sess.query(User.id).filter_by(id=7) rows2 = q2.all() - # EXPECTED_TYPE: List[.*Row[.*int].*] + # EXPECTED_TYPE: list[.*Row[.*int].*] reveal_type(rows2) # test #8280 diff --git a/test/typing/plain_files/orm/typed_queries.py b/test/typing/plain_files/orm/typed_queries.py index 252be918d8..424a03c8ae 100644 --- a/test/typing/plain_files/orm/typed_queries.py +++ b/test/typing/plain_files/orm/typed_queries.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import Tuple - from sqlalchemy import Column from sqlalchemy import column from sqlalchemy import create_engine @@ -133,14 +131,14 @@ def t_legacy_query_single_entity() -> None: # EXPECTED_TYPE: User reveal_type(q1.one()) - # EXPECTED_TYPE: List[User] + # EXPECTED_TYPE: list[User] reveal_type(q1.all()) # mypy switches to builtins.list for some reason here - # EXPECTED_RE_TYPE: .*\.[Ll]ist\[.*Row\*?\[.*User\].*\] + # EXPECTED_RE_TYPE: .*\.list\[.*Row\*?\[.*User\].*\] reveal_type(q1.only_return_tuples(True).all()) - # EXPECTED_TYPE: List[Tuple[User]] + # EXPECTED_TYPE: list[tuple[User]] reveal_type(q1.tuples().all()) @@ -172,7 +170,7 @@ def t_legacy_query_cols_tupleq_1() -> None: q2 = q1.tuples() - # EXPECTED_TYPE: Tuple[int, str] + # EXPECTED_TYPE: tuple[int, str] reveal_type(q2.one()) r1 = q2.one() @@ -383,7 +381,7 @@ def t_select_w_core_selectables() -> None: # this one unfortunately is not working in mypy. # pylance gets the correct type - # EXPECTED_TYPE: Select[Tuple[int, Any]] + # EXPECTED_TYPE: Select[tuple[int, Any]] # when experimenting with having a separate TypedSelect class for typing, # mypy would downgrade to Any rather than picking the basemost type. # with typing integrated into Select etc. we can at least get a Select @@ -392,9 +390,9 @@ def t_select_w_core_selectables() -> None: reveal_type(s2) # so a fully explicit type may be given - s2_typed: Select[Tuple[int, str]] = select(User.id, s1.c.name) + s2_typed: Select[tuple[int, str]] = select(User.id, s1.c.name) - # EXPECTED_TYPE: Select[Tuple[int, str]] + # EXPECTED_TYPE: Select[tuple[int, str]] reveal_type(s2_typed) # plain FromClause etc we at least get Select diff --git a/test/typing/plain_files/sql/typed_results.py b/test/typing/plain_files/sql/typed_results.py index 3c8b7f9134..498d2d276a 100644 --- a/test/typing/plain_files/sql/typed_results.py +++ b/test/typing/plain_files/sql/typed_results.py @@ -359,11 +359,11 @@ def t_connection_execute_multi_row_t() -> None: def t_connection_execute_multi() -> None: result = connection.execute(multi_stmt).t - # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.int\*?, builtins.str\*?\]\] + # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[tuple\[builtins.int\*?, builtins.str\*?\]\] reveal_type(result) row = result.one() - # EXPECTED_RE_TYPE: Tuple\[builtins.int\*?, builtins.str\*?\] + # EXPECTED_RE_TYPE: tuple\[builtins.int\*?, builtins.str\*?\] reveal_type(row) x, y = row @@ -378,11 +378,11 @@ def t_connection_execute_multi() -> None: def t_connection_execute_single() -> None: result = connection.execute(single_stmt).t - # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.str\*?\]\] + # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[tuple\[builtins.str\*?\]\] reveal_type(result) row = result.one() - # EXPECTED_RE_TYPE: Tuple\[builtins.str\*?\] + # EXPECTED_RE_TYPE: tuple\[builtins.str\*?\] reveal_type(row) (x,) = row @@ -394,7 +394,7 @@ def t_connection_execute_single() -> None: def t_connection_execute_single_row_scalar() -> None: result = connection.execute(single_stmt).t - # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.str\*?\]\] + # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[tuple\[builtins.str\*?\]\] reveal_type(result) x = result.scalar() @@ -424,11 +424,11 @@ def t_connection_scalars() -> None: def t_session_execute_multi() -> None: result = session.execute(multi_stmt).t - # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.int\*?, builtins.str\*?\]\] + # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[tuple\[builtins.int\*?, builtins.str\*?\]\] reveal_type(result) row = result.one() - # EXPECTED_RE_TYPE: Tuple\[builtins.int\*?, builtins.str\*?\] + # EXPECTED_RE_TYPE: tuple\[builtins.int\*?, builtins.str\*?\] reveal_type(row) x, y = row @@ -443,11 +443,11 @@ def t_session_execute_multi() -> None: def t_session_execute_single() -> None: result = session.execute(single_stmt).t - # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.str\*?\]\] + # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[tuple\[builtins.str\*?\]\] reveal_type(result) row = result.one() - # EXPECTED_RE_TYPE: Tuple\[builtins.str\*?\] + # EXPECTED_RE_TYPE: tuple\[builtins.str\*?\] reveal_type(row) (x,) = row @@ -477,11 +477,11 @@ def t_session_scalars() -> None: async def t_async_connection_execute_multi() -> None: result = (await async_connection.execute(multi_stmt)).t - # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.int\*?, builtins.str\*?\]\] + # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[tuple\[builtins.int\*?, builtins.str\*?\]\] reveal_type(result) row = result.one() - # EXPECTED_RE_TYPE: Tuple\[builtins.int\*?, builtins.str\*?\] + # EXPECTED_RE_TYPE: tuple\[builtins.int\*?, builtins.str\*?\] reveal_type(row) x, y = row @@ -496,12 +496,12 @@ async def t_async_connection_execute_multi() -> None: async def t_async_connection_execute_single() -> None: result = (await async_connection.execute(single_stmt)).t - # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.str\*?\]\] + # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[tuple\[builtins.str\*?\]\] reveal_type(result) row = result.one() - # EXPECTED_RE_TYPE: Tuple\[builtins.str\*?\] + # EXPECTED_RE_TYPE: tuple\[builtins.str\*?\] reveal_type(row) (x,) = row @@ -531,11 +531,11 @@ async def t_async_connection_scalars() -> None: async def t_async_session_execute_multi() -> None: result = (await async_session.execute(multi_stmt)).t - # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.int\*?, builtins.str\*?\]\] + # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[tuple\[builtins.int\*?, builtins.str\*?\]\] reveal_type(result) row = result.one() - # EXPECTED_RE_TYPE: Tuple\[builtins.int\*?, builtins.str\*?\] + # EXPECTED_RE_TYPE: tuple\[builtins.int\*?, builtins.str\*?\] reveal_type(row) x, y = row @@ -550,11 +550,11 @@ async def t_async_session_execute_multi() -> None: async def t_async_session_execute_single() -> None: result = (await async_session.execute(single_stmt)).t - # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.str\*?\]\] + # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[tuple\[builtins.str\*?\]\] reveal_type(result) row = result.one() - # EXPECTED_RE_TYPE: Tuple\[builtins.str\*?\] + # EXPECTED_RE_TYPE: tuple\[builtins.str\*?\] reveal_type(row) (x,) = row @@ -584,11 +584,11 @@ async def t_async_session_scalars() -> None: async def t_async_connection_stream_multi() -> None: result = (await async_connection.stream(multi_stmt)).t - # EXPECTED_RE_TYPE: sqlalchemy.*AsyncTupleResult\[Tuple\[builtins.int\*?, builtins.str\*?\]\] + # EXPECTED_RE_TYPE: sqlalchemy.*AsyncTupleResult\[tuple\[builtins.int\*?, builtins.str\*?\]\] reveal_type(result) row = await result.one() - # EXPECTED_RE_TYPE: Tuple\[builtins.int\*?, builtins.str\*?\] + # EXPECTED_RE_TYPE: tuple\[builtins.int\*?, builtins.str\*?\] reveal_type(row) x, y = row @@ -603,11 +603,11 @@ async def t_async_connection_stream_multi() -> None: async def t_async_connection_stream_single() -> None: result = (await async_connection.stream(single_stmt)).t - # EXPECTED_RE_TYPE: sqlalchemy.*AsyncTupleResult\[Tuple\[builtins.str\*?\]\] + # EXPECTED_RE_TYPE: sqlalchemy.*AsyncTupleResult\[tuple\[builtins.str\*?\]\] reveal_type(result) row = await result.one() - # EXPECTED_RE_TYPE: Tuple\[builtins.str\*?\] + # EXPECTED_RE_TYPE: tuple\[builtins.str\*?\] reveal_type(row) (x,) = row @@ -630,11 +630,11 @@ async def t_async_connection_stream_scalars() -> None: async def t_async_session_stream_multi() -> None: result = (await async_session.stream(multi_stmt)).t - # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.int\*?, builtins.str\*?\]\] + # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[tuple\[builtins.int\*?, builtins.str\*?\]\] reveal_type(result) row = await result.one() - # EXPECTED_RE_TYPE: Tuple\[builtins.int\*?, builtins.str\*?\] + # EXPECTED_RE_TYPE: tuple\[builtins.int\*?, builtins.str\*?\] reveal_type(row) x, y = row @@ -649,11 +649,11 @@ async def t_async_session_stream_multi() -> None: async def t_async_session_stream_single() -> None: result = (await async_session.stream(single_stmt)).t - # EXPECTED_RE_TYPE: sqlalchemy.*AsyncTupleResult\[Tuple\[builtins.str\*?\]\] + # EXPECTED_RE_TYPE: sqlalchemy.*AsyncTupleResult\[tuple\[builtins.str\*?\]\] reveal_type(result) row = await result.one() - # EXPECTED_RE_TYPE: Tuple\[builtins.str\*?\] + # EXPECTED_RE_TYPE: tuple\[builtins.str\*?\] reveal_type(row) (x,) = row diff --git a/tox.ini b/tox.ini index 0b4808e6b0..4ff125d62c 100644 --- a/tox.ini +++ b/tox.ini @@ -28,9 +28,9 @@ usedevelop= cov: True extras= - py{3,38,39,310,311,312,313}: {[greenletextras]extras} + py{3,39,310,311,312,313}: {[greenletextras]extras} - py{38,39,310}-sqlite_file: sqlcipher + py{39,310}-sqlite_file: sqlcipher postgresql: postgresql postgresql: postgresql_pg8000 postgresql: postgresql_psycopg @@ -125,7 +125,7 @@ setenv= sqlite-nogreenlet: EXTRA_SQLITE_DRIVERS={env:EXTRA_SQLITE_DRIVERS:--dbdriver sqlite --dbdriver pysqlite_numeric} - py{37,38,39}-sqlite_file: EXTRA_SQLITE_DRIVERS={env:EXTRA_SQLITE_DRIVERS:--dbdriver sqlite --dbdriver aiosqlite --dbdriver pysqlcipher} + py{39}-sqlite_file: EXTRA_SQLITE_DRIVERS={env:EXTRA_SQLITE_DRIVERS:--dbdriver sqlite --dbdriver aiosqlite --dbdriver pysqlcipher} # omit pysqlcipher for Python 3.10 py{3,310,311,312}-sqlite_file: EXTRA_SQLITE_DRIVERS={env:EXTRA_SQLITE_DRIVERS:--dbdriver sqlite --dbdriver aiosqlite}