From bb3c24595d503a1daf764e18efd8b85986808463 Mon Sep 17 00:00:00 2001 From: Federico Caselli Date: Thu, 21 Aug 2025 22:53:15 +0200 Subject: [PATCH] remove python 3.9 support since it's close to eol Fixes: #12819 Change-Id: I60f848226c1fef2a769845938d6afa3d3c5d0509 --- .github/workflows/create-wheels.yaml | 5 +- .github/workflows/run-on-pr.yaml | 2 +- .github/workflows/run-test.yaml | 12 +- .github/workflows/scripts/can_install.py | 25 --- doc/build/changelog/unreleased_21/10357.rst | 6 - .../unreleased_21/python_version.rst | 6 + doc/build/intro.rst | 4 +- lib/sqlalchemy/dialects/mssql/base.py | 2 +- lib/sqlalchemy/dialects/postgresql/asyncpg.py | 3 +- lib/sqlalchemy/dialects/postgresql/psycopg.py | 3 +- lib/sqlalchemy/dialects/postgresql/ranges.py | 40 +--- lib/sqlalchemy/engine/create.py | 3 +- lib/sqlalchemy/engine/cursor.py | 2 +- lib/sqlalchemy/engine/default.py | 2 +- lib/sqlalchemy/engine/events.py | 2 +- lib/sqlalchemy/engine/interfaces.py | 2 +- lib/sqlalchemy/engine/result.py | 2 +- lib/sqlalchemy/event/base.py | 2 +- lib/sqlalchemy/ext/associationproxy.py | 2 +- lib/sqlalchemy/ext/asyncio/base.py | 6 +- lib/sqlalchemy/ext/asyncio/engine.py | 4 +- lib/sqlalchemy/ext/asyncio/result.py | 2 +- lib/sqlalchemy/ext/asyncio/session.py | 4 +- lib/sqlalchemy/ext/hybrid.py | 6 +- lib/sqlalchemy/inspection.py | 2 +- lib/sqlalchemy/log.py | 2 +- lib/sqlalchemy/orm/_orm_constructors.py | 4 +- lib/sqlalchemy/orm/_typing.py | 2 +- lib/sqlalchemy/orm/attributes.py | 4 +- lib/sqlalchemy/orm/base.py | 2 +- lib/sqlalchemy/orm/bulk_persistence.py | 2 +- lib/sqlalchemy/orm/decl_api.py | 2 +- lib/sqlalchemy/orm/decl_base.py | 2 +- lib/sqlalchemy/orm/descriptor_props.py | 2 +- lib/sqlalchemy/orm/instrumentation.py | 2 +- lib/sqlalchemy/orm/mapped_collection.py | 2 +- lib/sqlalchemy/orm/mapper.py | 2 +- lib/sqlalchemy/orm/path_registry.py | 3 +- lib/sqlalchemy/orm/properties.py | 2 +- lib/sqlalchemy/orm/query.py | 2 +- lib/sqlalchemy/orm/relationships.py | 2 +- lib/sqlalchemy/orm/session.py | 2 +- lib/sqlalchemy/orm/state.py | 2 +- lib/sqlalchemy/orm/state_changes.py | 2 +- lib/sqlalchemy/orm/strategies.py | 2 +- lib/sqlalchemy/orm/strategy_options.py | 2 +- lib/sqlalchemy/orm/util.py | 4 +- lib/sqlalchemy/orm/writeonly.py | 2 +- lib/sqlalchemy/pool/base.py | 2 +- lib/sqlalchemy/pool/impl.py | 2 +- lib/sqlalchemy/sql/_elements_constructors.py | 2 +- lib/sqlalchemy/sql/_orm_types.py | 2 +- lib/sqlalchemy/sql/_typing.py | 6 +- lib/sqlalchemy/sql/_util_cy.py | 3 +- lib/sqlalchemy/sql/annotation.py | 2 +- lib/sqlalchemy/sql/base.py | 2 +- lib/sqlalchemy/sql/cache_key.py | 2 +- lib/sqlalchemy/sql/coercions.py | 2 +- lib/sqlalchemy/sql/compiler.py | 2 +- lib/sqlalchemy/sql/crud.py | 2 +- lib/sqlalchemy/sql/dml.py | 2 +- lib/sqlalchemy/sql/elements.py | 4 +- lib/sqlalchemy/sql/lambdas.py | 2 +- lib/sqlalchemy/sql/operators.py | 2 +- lib/sqlalchemy/sql/roles.py | 2 +- lib/sqlalchemy/sql/schema.py | 4 +- lib/sqlalchemy/sql/selectable.py | 2 +- lib/sqlalchemy/sql/sqltypes.py | 4 +- lib/sqlalchemy/sql/type_api.py | 2 +- lib/sqlalchemy/sql/util.py | 2 +- lib/sqlalchemy/sql/visitors.py | 2 +- lib/sqlalchemy/testing/engines.py | 2 +- lib/sqlalchemy/testing/fixtures/mypy.py | 5 +- lib/sqlalchemy/testing/requirements.py | 6 - lib/sqlalchemy/util/__init__.py | 3 - lib/sqlalchemy/util/_collections.py | 2 +- lib/sqlalchemy/util/compat.py | 23 -- lib/sqlalchemy/util/concurrency.py | 4 +- lib/sqlalchemy/util/langhelpers.py | 21 +- lib/sqlalchemy/util/typing.py | 41 +--- pyproject.toml | 5 +- test/base/test_tutorials.py | 2 +- test/base/test_typing_utils.py | 29 +-- test/ext/asyncio/test_engine_py3k.py | 1 - test/ext/asyncio/test_session_py3k.py | 1 - test/ext/test_associationproxy.py | 2 +- test/orm/declarative/test_dc_transforms.py | 26 ++- .../test_dc_transforms_future_anno_sync.py | 26 ++- test/orm/declarative/test_mixin.py | 3 +- .../test_tm_future_annotations_sync.py | 207 ++++++------------ test/orm/declarative/test_typed_mapping.py | 207 ++++++------------ test/orm/test_cache_key.py | 4 +- test/orm/test_deprecations.py | 3 +- 93 files changed, 303 insertions(+), 579 deletions(-) delete mode 100644 .github/workflows/scripts/can_install.py delete mode 100644 doc/build/changelog/unreleased_21/10357.rst create mode 100644 doc/build/changelog/unreleased_21/python_version.rst diff --git a/.github/workflows/create-wheels.yaml b/.github/workflows/create-wheels.yaml index 6c4d7dd42b..ce81123d7c 100644 --- a/.github/workflows/create-wheels.yaml +++ b/.github/workflows/create-wheels.yaml @@ -20,7 +20,6 @@ jobs: matrix: # emulated wheels on linux take too much time, split wheels into multiple runs python: - - "cp39-*" - "cp310-* cp311-*" - "cp312-* cp313-*" wheel_mode: @@ -41,7 +40,7 @@ jobs: # create pure python build - os: ubuntu-22.04 wheel_mode: pure-python - python: "cp-312*" + python: "cp-313*" exclude: - os: "windows-2022" @@ -93,7 +92,7 @@ jobs: - name: Set up Python for twine and pure-python wheel uses: actions/setup-python@v5 with: - python-version: "3.12" + python-version: "3.13" - name: Build pure-python wheel if: ${{ matrix.wheel_mode == 'pure-python' && runner.os == 'Linux' }} diff --git a/.github/workflows/run-on-pr.yaml b/.github/workflows/run-on-pr.yaml index 889da8499f..00cacf48d6 100644 --- a/.github/workflows/run-on-pr.yaml +++ b/.github/workflows/run-on-pr.yaml @@ -62,7 +62,7 @@ jobs: os: - "ubuntu-22.04" python-version: - - "3.12" + - "3.13" tox-env: - mypy - lint diff --git a/.github/workflows/run-test.yaml b/.github/workflows/run-test.yaml index cd214febd1..9818625603 100644 --- a/.github/workflows/run-test.yaml +++ b/.github/workflows/run-test.yaml @@ -33,11 +33,11 @@ jobs: - "macos-latest" - "macos-13" python-version: - - "3.9" - "3.10" - "3.11" - "3.12" - "3.13" + - "3.14.0-alpha - 3.14" - "pypy-3.10" build-type: - "cext" @@ -71,13 +71,11 @@ jobs: # windows des not have arm64 python - os: "windows-latest" architecture: arm64 - # macos: latests uses arm macs. only 3.10+; no x86/x64 + # macos: latests uses arm macs. no x86/x64 - os: "macos-latest" architecture: x86 - os: "macos-latest" architecture: x64 - - os: "macos-latest" - python-version: "3.9" # macos 13: uses intel macs. no arm64, x86 - os: "macos-13" architecture: arm64 @@ -96,8 +94,6 @@ jobs: python-version: "pypy-3.10" - os: "windows-11-arm" python-version: "3.10" - - os: "windows-11-arm" - python-version: "3.9" - os: "windows-11-arm" architecture: x86 - os: "windows-11-arm" @@ -150,9 +146,9 @@ jobs: - pep484 include: - # run lint only on 3.12 + # run lint only on 3.13 - tox-env: lint - python-version: "3.12" + python-version: "3.13" os: "ubuntu-22.04" fail-fast: false diff --git a/.github/workflows/scripts/can_install.py b/.github/workflows/scripts/can_install.py deleted file mode 100644 index ecb24b5623..0000000000 --- a/.github/workflows/scripts/can_install.py +++ /dev/null @@ -1,25 +0,0 @@ -import sys - -from packaging import tags - -to_check = "--" -found = False -if len(sys.argv) > 1: - to_check = sys.argv[1] - for t in tags.sys_tags(): - start = "-".join(str(t).split("-")[:2]) - if to_check.lower() == start: - print( - "Wheel tag {0} matches installed version {1}.".format( - to_check, t - ) - ) - found = True - break -if not found: - print( - "Wheel tag {0} not found in installed version tags {1}.".format( - to_check, [str(t) for t in tags.sys_tags()] - ) - ) - exit(1) diff --git a/doc/build/changelog/unreleased_21/10357.rst b/doc/build/changelog/unreleased_21/10357.rst deleted file mode 100644 index 22772678fa..0000000000 --- a/doc/build/changelog/unreleased_21/10357.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. change:: - :tags: change, installation - :tickets: 10357, 12029 - - 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/changelog/unreleased_21/python_version.rst b/doc/build/changelog/unreleased_21/python_version.rst new file mode 100644 index 0000000000..e936563846 --- /dev/null +++ b/doc/build/changelog/unreleased_21/python_version.rst @@ -0,0 +1,6 @@ +.. change:: + :tags: change, installation + :tickets: 10357, 12029, 12819 + + Python 3.10 or above is now required; support for Python 3.9, 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 cba95ab69e..2c68b5489a 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.9 and higher +* cPython 3.10 and higher * Python-3 compatible versions of `PyPy `_ .. versionchanged:: 2.1 - SQLAlchemy now targets Python 3.9 and above. + SQLAlchemy now targets Python 3.10 and above. Supported Installation Methods diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index c0bf43304a..b655c214da 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -963,6 +963,7 @@ import codecs import datetime import operator import re +from typing import Literal from typing import overload from typing import TYPE_CHECKING from uuid import UUID as _python_UUID @@ -1010,7 +1011,6 @@ from ...types import SMALLINT from ...types import TEXT from ...types import VARCHAR from ...util import update_wrapper -from ...util.typing import Literal if TYPE_CHECKING: from ...sql.dml import DMLState diff --git a/lib/sqlalchemy/dialects/postgresql/asyncpg.py b/lib/sqlalchemy/dialects/postgresql/asyncpg.py index 09ff9f48c0..1f11984eaf 100644 --- a/lib/sqlalchemy/dialects/postgresql/asyncpg.py +++ b/lib/sqlalchemy/dialects/postgresql/asyncpg.py @@ -184,6 +184,7 @@ import decimal import json as _py_json import re import time +from types import NoneType from typing import Any from typing import Awaitable from typing import Callable @@ -435,8 +436,6 @@ class _AsyncpgMultiRange(ranges.AbstractMultiRangeImpl): def bind_processor(self, dialect): asyncpg_Range = dialect.dbapi.asyncpg.Range - NoneType = type(None) - def to_range(value): if isinstance(value, (str, NoneType)): return value diff --git a/lib/sqlalchemy/dialects/postgresql/psycopg.py b/lib/sqlalchemy/dialects/postgresql/psycopg.py index 7ad63a2fd3..966195752d 100644 --- a/lib/sqlalchemy/dialects/postgresql/psycopg.py +++ b/lib/sqlalchemy/dialects/postgresql/psycopg.py @@ -97,6 +97,7 @@ from __future__ import annotations import collections import logging import re +from types import NoneType from typing import cast from typing import TYPE_CHECKING @@ -236,8 +237,6 @@ class _PsycopgMultiRange(ranges.AbstractMultiRangeImpl): PGDialect_psycopg, dialect )._psycopg_Multirange - NoneType = type(None) - def to_range(value): if isinstance(value, (str, NoneType, psycopg_Multirange)): return value diff --git a/lib/sqlalchemy/dialects/postgresql/ranges.py b/lib/sqlalchemy/dialects/postgresql/ranges.py index 0ce4ea2913..ea25ed5caf 100644 --- a/lib/sqlalchemy/dialects/postgresql/ranges.py +++ b/lib/sqlalchemy/dialects/postgresql/ranges.py @@ -16,6 +16,7 @@ from typing import Any from typing import cast from typing import Generic from typing import List +from typing import Literal from typing import Optional from typing import overload from typing import Sequence @@ -36,8 +37,6 @@ from .operators import STRICTLY_RIGHT_OF from ... import types as sqltypes from ...sql import operators from ...sql.type_api import TypeEngine -from ...util import py310 -from ...util.typing import Literal if TYPE_CHECKING: from ...sql.elements import ColumnElement @@ -48,15 +47,8 @@ _T = TypeVar("_T", bound=Any) _BoundsType = Literal["()", "[)", "(]", "[]"] -if py310: - dc_slots = {"slots": True} - dc_kwonly = {"kw_only": True} -else: - dc_slots = {} - dc_kwonly = {} - -@dataclasses.dataclass(frozen=True, **dc_slots) +@dataclasses.dataclass(frozen=True, slots=True) class Range(Generic[_T]): """Represent a PostgreSQL range. @@ -85,32 +77,8 @@ class Range(Generic[_T]): upper: Optional[_T] = None """the upper bound""" - if TYPE_CHECKING: - bounds: _BoundsType = dataclasses.field(default="[)") - empty: bool = dataclasses.field(default=False) - else: - bounds: _BoundsType = dataclasses.field(default="[)", **dc_kwonly) - empty: bool = dataclasses.field(default=False, **dc_kwonly) - - if not py310: - - def __init__( - self, - lower: Optional[_T] = None, - upper: Optional[_T] = None, - *, - bounds: _BoundsType = "[)", - empty: bool = False, - ): - # no __slots__ either so we can update dict - self.__dict__.update( - { - "lower": lower, - "upper": upper, - "bounds": bounds, - "empty": empty, - } - ) + bounds: _BoundsType = dataclasses.field(default="[)", kw_only=True) + empty: bool = dataclasses.field(default=False, kw_only=True) def __bool__(self) -> bool: return not self.empty diff --git a/lib/sqlalchemy/engine/create.py b/lib/sqlalchemy/engine/create.py index d2bf7702c7..948a3d72b3 100644 --- a/lib/sqlalchemy/engine/create.py +++ b/lib/sqlalchemy/engine/create.py @@ -32,6 +32,8 @@ from ..sql import compiler from ..util import immutabledict if typing.TYPE_CHECKING: + from typing import Literal + from .base import Engine from .interfaces import _ExecuteOptions from .interfaces import _ParamStyle @@ -42,7 +44,6 @@ if typing.TYPE_CHECKING: from ..pool import _CreatorWRecFnType from ..pool import _ResetStyleArgType from ..pool import Pool - from ..util.typing import Literal @overload diff --git a/lib/sqlalchemy/engine/cursor.py b/lib/sqlalchemy/engine/cursor.py index 165ae2feaa..35d8180ac0 100644 --- a/lib/sqlalchemy/engine/cursor.py +++ b/lib/sqlalchemy/engine/cursor.py @@ -23,6 +23,7 @@ from typing import Dict from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import Mapping from typing import NoReturn from typing import Optional @@ -50,7 +51,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.typing import Literal from ..util.typing import Self from ..util.typing import TupleAny from ..util.typing import TypeVarTuple diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index c624cece19..fcdb68093a 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -28,6 +28,7 @@ from typing import cast from typing import Dict from typing import Final from typing import List +from typing import Literal from typing import Mapping from typing import MutableMapping from typing import MutableSequence @@ -66,7 +67,6 @@ from ..sql.compiler import DDLCompiler from ..sql.compiler import InsertmanyvaluesSentinelOpts from ..sql.compiler import SQLCompiler from ..sql.elements import quoted_name -from ..util.typing import Literal from ..util.typing import TupleAny from ..util.typing import Unpack diff --git a/lib/sqlalchemy/engine/events.py b/lib/sqlalchemy/engine/events.py index fab3cb3040..d5c809439c 100644 --- a/lib/sqlalchemy/engine/events.py +++ b/lib/sqlalchemy/engine/events.py @@ -11,6 +11,7 @@ from __future__ import annotations import typing from typing import Any from typing import Dict +from typing import Literal from typing import Optional from typing import Tuple from typing import Type @@ -24,7 +25,6 @@ from .interfaces import DBAPICursor from .interfaces import Dialect from .. import event from .. import exc -from ..util.typing import Literal from ..util.typing import TupleAny from ..util.typing import Unpack diff --git a/lib/sqlalchemy/engine/interfaces.py b/lib/sqlalchemy/engine/interfaces.py index 5d7f5c9785..0c998996a1 100644 --- a/lib/sqlalchemy/engine/interfaces.py +++ b/lib/sqlalchemy/engine/interfaces.py @@ -19,6 +19,7 @@ from typing import Dict from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import Mapping from typing import MutableMapping from typing import Optional @@ -42,7 +43,6 @@ from ..sql.compiler import TypeCompiler as TypeCompiler from ..sql.compiler import TypeCompiler # noqa from ..util import immutabledict from ..util.concurrency import await_ -from ..util.typing import Literal from ..util.typing import NotRequired if TYPE_CHECKING: diff --git a/lib/sqlalchemy/engine/result.py b/lib/sqlalchemy/engine/result.py index 7c72f288f6..844db160f6 100644 --- a/lib/sqlalchemy/engine/result.py +++ b/lib/sqlalchemy/engine/result.py @@ -22,6 +22,7 @@ from typing import Generic from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import Mapping from typing import NoReturn from typing import Optional @@ -44,7 +45,6 @@ from ..sql.base import InPlaceGenerative from ..util import deprecated from ..util import HasMemoized_ro_memoized_attribute from ..util import NONE_SET -from ..util.typing import Literal from ..util.typing import Self from ..util.typing import TupleAny from ..util.typing import TypeVarTuple diff --git a/lib/sqlalchemy/event/base.py b/lib/sqlalchemy/event/base.py index 66dc12996b..e1251cb45c 100644 --- a/lib/sqlalchemy/event/base.py +++ b/lib/sqlalchemy/event/base.py @@ -24,6 +24,7 @@ from typing import Dict from typing import Generic from typing import Iterator from typing import List +from typing import Literal from typing import Mapping from typing import MutableMapping from typing import Optional @@ -40,7 +41,6 @@ from .attr import _JoinedListener from .registry import _ET from .registry import _EventKey from .. import util -from ..util.typing import Literal _registrars: MutableMapping[str, List[Type[_HasEventsDispatch[Any]]]] = ( util.defaultdict(list) diff --git a/lib/sqlalchemy/ext/associationproxy.py b/lib/sqlalchemy/ext/associationproxy.py index 22d2bb570d..f7bb0f994c 100644 --- a/lib/sqlalchemy/ext/associationproxy.py +++ b/lib/sqlalchemy/ext/associationproxy.py @@ -29,6 +29,7 @@ from typing import Iterable from typing import Iterator from typing import KeysView from typing import List +from typing import Literal from typing import Mapping from typing import MutableMapping from typing import MutableSequence @@ -61,7 +62,6 @@ from ..orm.interfaces import _DEFAULT_ATTRIBUTE_OPTIONS from ..sql import operators from ..sql import or_ from ..sql.base import _NoArg -from ..util.typing import Literal from ..util.typing import Self from ..util.typing import SupportsKeysAndGetItem diff --git a/lib/sqlalchemy/ext/asyncio/base.py b/lib/sqlalchemy/ext/asyncio/base.py index 72a617f4e2..cc22950a57 100644 --- a/lib/sqlalchemy/ext/asyncio/base.py +++ b/lib/sqlalchemy/ext/asyncio/base.py @@ -18,6 +18,7 @@ from typing import ClassVar from typing import Dict from typing import Generator from typing import Generic +from typing import Literal from typing import NoReturn from typing import Optional from typing import overload @@ -27,7 +28,6 @@ import weakref from . import exc as async_exc from ... import util -from ...util.typing import Literal from ...util.typing import Self _T = TypeVar("_T", bound=Any) @@ -148,7 +148,7 @@ class GeneratorStartableContext(StartableContext[_T_co]): async def start(self, is_ctxmanager: bool = False) -> _T_co: try: - start_value = await util.anext_(self.gen) + start_value = await anext(self.gen) except StopAsyncIteration: raise RuntimeError("generator didn't yield") from None @@ -167,7 +167,7 @@ class GeneratorStartableContext(StartableContext[_T_co]): # vendored from contextlib.py if typ is None: try: - await util.anext_(self.gen) + await anext(self.gen) except StopAsyncIteration: return False else: diff --git a/lib/sqlalchemy/ext/asyncio/engine.py b/lib/sqlalchemy/ext/asyncio/engine.py index a339113210..5f5ec6cf35 100644 --- a/lib/sqlalchemy/ext/asyncio/engine.py +++ b/lib/sqlalchemy/ext/asyncio/engine.py @@ -11,11 +11,13 @@ import contextlib from typing import Any from typing import AsyncIterator from typing import Callable +from typing import Concatenate from typing import Dict from typing import Generator from typing import NoReturn from typing import Optional from typing import overload +from typing import ParamSpec from typing import Type from typing import TYPE_CHECKING from typing import TypeVar @@ -40,8 +42,6 @@ from ...engine.base import NestedTransaction from ...engine.base import Transaction from ...exc import ArgumentError from ...util.concurrency import greenlet_spawn -from ...util.typing import Concatenate -from ...util.typing import ParamSpec from ...util.typing import TupleAny from ...util.typing import TypeVarTuple from ...util.typing import Unpack diff --git a/lib/sqlalchemy/ext/asyncio/result.py b/lib/sqlalchemy/ext/asyncio/result.py index 970bb791bc..2e2efebb5a 100644 --- a/lib/sqlalchemy/ext/asyncio/result.py +++ b/lib/sqlalchemy/ext/asyncio/result.py @@ -9,6 +9,7 @@ from __future__ import annotations import operator from typing import Any from typing import AsyncIterator +from typing import Literal from typing import Optional from typing import overload from typing import Sequence @@ -30,7 +31,6 @@ from ...engine.row import RowMapping from ...sql.base import _generative from ...util import deprecated from ...util.concurrency import greenlet_spawn -from ...util.typing import Literal from ...util.typing import Self from ...util.typing import TupleAny from ...util.typing import TypeVarTuple diff --git a/lib/sqlalchemy/ext/asyncio/session.py b/lib/sqlalchemy/ext/asyncio/session.py index 77c7b6edae..58958b7f10 100644 --- a/lib/sqlalchemy/ext/asyncio/session.py +++ b/lib/sqlalchemy/ext/asyncio/session.py @@ -11,6 +11,7 @@ from typing import Any from typing import Awaitable from typing import Callable from typing import cast +from typing import Concatenate from typing import Dict from typing import Generic from typing import Iterable @@ -18,6 +19,7 @@ from typing import Iterator from typing import NoReturn from typing import Optional from typing import overload +from typing import ParamSpec from typing import Sequence from typing import Tuple from typing import Type @@ -38,8 +40,6 @@ from ...orm import Session from ...orm import SessionTransaction from ...orm import state as _instance_state from ...util.concurrency import greenlet_spawn -from ...util.typing import Concatenate -from ...util.typing import ParamSpec from ...util.typing import TupleAny from ...util.typing import TypeVarTuple from ...util.typing import Unpack diff --git a/lib/sqlalchemy/ext/hybrid.py b/lib/sqlalchemy/ext/hybrid.py index 840a3f0650..58b5a80bb6 100644 --- a/lib/sqlalchemy/ext/hybrid.py +++ b/lib/sqlalchemy/ext/hybrid.py @@ -1146,11 +1146,14 @@ from __future__ import annotations from typing import Any from typing import Callable from typing import cast +from typing import Concatenate from typing import Generic from typing import List +from typing import Literal from typing import MutableMapping from typing import Optional from typing import overload +from typing import ParamSpec from typing import Protocol from typing import Sequence from typing import Tuple @@ -1170,9 +1173,6 @@ from ..sql import roles from ..sql._typing import is_has_clause_element from ..sql.elements import ColumnElement from ..sql.elements import SQLCoreOperations -from ..util.typing import Concatenate -from ..util.typing import Literal -from ..util.typing import ParamSpec from ..util.typing import Self if TYPE_CHECKING: diff --git a/lib/sqlalchemy/inspection.py b/lib/sqlalchemy/inspection.py index 7191167166..04adc82693 100644 --- a/lib/sqlalchemy/inspection.py +++ b/lib/sqlalchemy/inspection.py @@ -34,6 +34,7 @@ from typing import Any from typing import Callable from typing import Dict from typing import Generic +from typing import Literal from typing import Optional from typing import overload from typing import Protocol @@ -42,7 +43,6 @@ from typing import TypeVar from typing import Union from . import exc -from .util.typing import Literal _T = TypeVar("_T", bound=Any) _TCov = TypeVar("_TCov", bound=Any, covariant=True) diff --git a/lib/sqlalchemy/log.py b/lib/sqlalchemy/log.py index b9627d879c..4e676239b7 100644 --- a/lib/sqlalchemy/log.py +++ b/lib/sqlalchemy/log.py @@ -22,6 +22,7 @@ from __future__ import annotations import logging import sys from typing import Any +from typing import Literal from typing import Optional from typing import overload from typing import Set @@ -30,7 +31,6 @@ from typing import TypeVar from typing import Union from .util import py311 -from .util.typing import Literal STACKLEVEL = True diff --git a/lib/sqlalchemy/orm/_orm_constructors.py b/lib/sqlalchemy/orm/_orm_constructors.py index bcd21fc964..1bc9f17035 100644 --- a/lib/sqlalchemy/orm/_orm_constructors.py +++ b/lib/sqlalchemy/orm/_orm_constructors.py @@ -8,10 +8,12 @@ from __future__ import annotations import typing +from typing import Annotated from typing import Any from typing import Callable from typing import Collection from typing import Iterable +from typing import Literal from typing import Mapping from typing import NoReturn from typing import Optional @@ -47,8 +49,6 @@ from ..sql.base import SchemaEventTarget from ..sql.schema import _InsertSentinelColumnDefault from ..sql.schema import SchemaConst from ..sql.selectable import FromClause -from ..util.typing import Annotated -from ..util.typing import Literal if TYPE_CHECKING: from ._typing import _EntityType diff --git a/lib/sqlalchemy/orm/_typing.py b/lib/sqlalchemy/orm/_typing.py index 8cf5335d67..80f4cb1448 100644 --- a/lib/sqlalchemy/orm/_typing.py +++ b/lib/sqlalchemy/orm/_typing.py @@ -16,6 +16,7 @@ from typing import Protocol from typing import Tuple from typing import Type from typing import TYPE_CHECKING +from typing import TypeGuard from typing import TypeVar from typing import Union @@ -27,7 +28,6 @@ from ..sql._orm_types import ( ) from ..sql._typing import _HasClauseElement from ..sql.elements import ColumnElement -from ..util.typing import TypeGuard if TYPE_CHECKING: from .attributes import _AttributeImpl diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 46462049cc..4cff73851e 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -26,6 +26,7 @@ from typing import ClassVar from typing import Dict from typing import Iterable from typing import List +from typing import Literal from typing import NamedTuple from typing import Optional from typing import overload @@ -33,6 +34,7 @@ from typing import Sequence from typing import Tuple from typing import Type from typing import TYPE_CHECKING +from typing import TypeGuard from typing import TypeVar from typing import Union @@ -90,9 +92,7 @@ from ..sql import visitors from ..sql.cache_key import HasCacheKey from ..sql.visitors import _TraverseInternalsType from ..sql.visitors import InternalTraversal -from ..util.typing import Literal from ..util.typing import Self -from ..util.typing import TypeGuard if TYPE_CHECKING: from ._typing import _EntityType diff --git a/lib/sqlalchemy/orm/base.py b/lib/sqlalchemy/orm/base.py index c53ba44345..32307222f7 100644 --- a/lib/sqlalchemy/orm/base.py +++ b/lib/sqlalchemy/orm/base.py @@ -16,6 +16,7 @@ from typing import Any from typing import Callable from typing import Dict from typing import Generic +from typing import Literal from typing import no_type_check from typing import Optional from typing import overload @@ -35,7 +36,6 @@ from ..sql.elements import SQLColumnExpression from ..sql.elements import SQLCoreOperations from ..util import FastIntFlag from ..util.langhelpers import TypingOnly -from ..util.typing import Literal if typing.TYPE_CHECKING: from ._typing import _EntityType diff --git a/lib/sqlalchemy/orm/bulk_persistence.py b/lib/sqlalchemy/orm/bulk_persistence.py index 8e813d667a..99b97ccf4c 100644 --- a/lib/sqlalchemy/orm/bulk_persistence.py +++ b/lib/sqlalchemy/orm/bulk_persistence.py @@ -18,6 +18,7 @@ from typing import Any from typing import cast from typing import Dict from typing import Iterable +from typing import Literal from typing import Optional from typing import overload from typing import TYPE_CHECKING @@ -53,7 +54,6 @@ from ..sql.dml import DeleteDMLState from ..sql.dml import InsertDMLState from ..sql.dml import UpdateDMLState from ..util import EMPTY_DICT -from ..util.typing import Literal from ..util.typing import TupleAny from ..util.typing import Unpack diff --git a/lib/sqlalchemy/orm/decl_api.py b/lib/sqlalchemy/orm/decl_api.py index 6239263bd3..b99906ed61 100644 --- a/lib/sqlalchemy/orm/decl_api.py +++ b/lib/sqlalchemy/orm/decl_api.py @@ -19,6 +19,7 @@ from typing import FrozenSet from typing import Generic from typing import Iterable from typing import Iterator +from typing import Literal from typing import Mapping from typing import Optional from typing import overload @@ -74,7 +75,6 @@ from ..util.typing import CallableReference from ..util.typing import de_optionalize_union_types from ..util.typing import is_generic from ..util.typing import is_literal -from ..util.typing import Literal from ..util.typing import LITERAL_TYPES from ..util.typing import Self diff --git a/lib/sqlalchemy/orm/decl_base.py b/lib/sqlalchemy/orm/decl_base.py index 9d9538bdf0..5d110e969b 100644 --- a/lib/sqlalchemy/orm/decl_base.py +++ b/lib/sqlalchemy/orm/decl_base.py @@ -16,6 +16,7 @@ from typing import Any from typing import Callable from typing import cast from typing import Dict +from typing import get_args from typing import Iterable from typing import List from typing import Mapping @@ -67,7 +68,6 @@ from ..sql.schema import Column from ..sql.schema import Table from ..util import topological from ..util.typing import _AnnotationScanType -from ..util.typing import get_args from ..util.typing import is_fwd_ref from ..util.typing import is_literal diff --git a/lib/sqlalchemy/orm/descriptor_props.py b/lib/sqlalchemy/orm/descriptor_props.py index 62cb5afc7c..99e9b4ddb1 100644 --- a/lib/sqlalchemy/orm/descriptor_props.py +++ b/lib/sqlalchemy/orm/descriptor_props.py @@ -20,6 +20,7 @@ import typing from typing import Any from typing import Callable from typing import Dict +from typing import get_args from typing import List from typing import NoReturn from typing import Optional @@ -55,7 +56,6 @@ from ..sql import expression from ..sql import operators from ..sql.base import _NoArg from ..sql.elements import BindParameter -from ..util.typing import get_args from ..util.typing import is_fwd_ref from ..util.typing import is_pep593 from ..util.typing import TupleAny diff --git a/lib/sqlalchemy/orm/instrumentation.py b/lib/sqlalchemy/orm/instrumentation.py index c95d0a0673..6e3a218cfd 100644 --- a/lib/sqlalchemy/orm/instrumentation.py +++ b/lib/sqlalchemy/orm/instrumentation.py @@ -34,6 +34,7 @@ from typing import Dict from typing import Generic from typing import Iterable from typing import List +from typing import Literal from typing import Optional from typing import Protocol from typing import Set @@ -54,7 +55,6 @@ from .attributes import _is_collection_attribute_impl from .. import util from ..event import EventTarget from ..util import HasMemoized -from ..util.typing import Literal if TYPE_CHECKING: from ._typing import _RegistryType diff --git a/lib/sqlalchemy/orm/mapped_collection.py b/lib/sqlalchemy/orm/mapped_collection.py index ca085c4037..a5885fc9d0 100644 --- a/lib/sqlalchemy/orm/mapped_collection.py +++ b/lib/sqlalchemy/orm/mapped_collection.py @@ -13,6 +13,7 @@ from typing import Callable from typing import Dict from typing import Generic from typing import List +from typing import Literal from typing import Optional from typing import Sequence from typing import Tuple @@ -31,7 +32,6 @@ from ..sql import expression from ..sql import roles from ..util.langhelpers import Missing from ..util.langhelpers import MissingOr -from ..util.typing import Literal if TYPE_CHECKING: from . import AttributeEventToken diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 9bc5cc055d..33d6646b35 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -33,6 +33,7 @@ from typing import Generic from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import Mapping from typing import Optional from typing import Sequence @@ -88,7 +89,6 @@ from ..sql.schema import Table from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL from ..util import HasMemoized from ..util import HasMemoized_ro_memoized_attribute -from ..util.typing import Literal from ..util.typing import TupleAny from ..util.typing import Unpack diff --git a/lib/sqlalchemy/orm/path_registry.py b/lib/sqlalchemy/orm/path_registry.py index d9e0226863..ff6a2dff21 100644 --- a/lib/sqlalchemy/orm/path_registry.py +++ b/lib/sqlalchemy/orm/path_registry.py @@ -32,6 +32,8 @@ from ..sql import visitors from ..sql.cache_key import HasCacheKey if TYPE_CHECKING: + from typing import TypeGuard + from ._typing import _InternalEntityType from .interfaces import StrategizedProperty from .mapper import Mapper @@ -41,7 +43,6 @@ if TYPE_CHECKING: from ..sql.elements import BindParameter from ..sql.visitors import anon_map from ..util.typing import _LiteralStar - from ..util.typing import TypeGuard def is_root(path: PathRegistry) -> TypeGuard[RootRegistry]: ... diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index bc0c8fdda3..5c5cac7ff4 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -17,6 +17,7 @@ from __future__ import annotations from typing import Any from typing import cast from typing import Dict +from typing import get_args from typing import List from typing import Optional from typing import Sequence @@ -56,7 +57,6 @@ from ..sql.schema import Column from ..sql.schema import SchemaConst from ..sql.type_api import TypeEngine from ..util.typing import de_optionalize_union_types -from ..util.typing import get_args from ..util.typing import includes_none from ..util.typing import is_a_type from ..util.typing import is_fwd_ref diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index c5a9fe9ddc..2eb2c5e008 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -30,6 +30,7 @@ from typing import Generic from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import Mapping from typing import Optional from typing import overload @@ -92,7 +93,6 @@ from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL from ..sql.selectable import SelectLabelStyle from ..util import deprecated from ..util import warn_deprecated -from ..util.typing import Literal from ..util.typing import Self from ..util.typing import TupleAny from ..util.typing import TypeVarTuple diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py index eba022685c..b608c52016 100644 --- a/lib/sqlalchemy/orm/relationships.py +++ b/lib/sqlalchemy/orm/relationships.py @@ -32,6 +32,7 @@ from typing import Generic from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import NamedTuple from typing import NoReturn from typing import Optional @@ -94,7 +95,6 @@ from ..sql.util import join_condition from ..sql.util import selectables_overlap from ..sql.util import visit_binary_product from ..util.typing import de_optionalize_union_types -from ..util.typing import Literal from ..util.typing import resolve_name_to_real_class_name if typing.TYPE_CHECKING: diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index e4383c21cf..8c9a0daee8 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -22,6 +22,7 @@ from typing import Generic from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import NoReturn from typing import Optional from typing import overload @@ -91,7 +92,6 @@ from ..sql.selectable import ForUpdateArg from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL from ..util import deprecated_params from ..util import IdentitySet -from ..util.typing import Literal from ..util.typing import TupleAny from ..util.typing import TypeVarTuple from ..util.typing import Unpack diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py index 0f879f3d1e..34575c56b8 100644 --- a/lib/sqlalchemy/orm/state.py +++ b/lib/sqlalchemy/orm/state.py @@ -19,6 +19,7 @@ from typing import Callable from typing import Dict from typing import Generic from typing import Iterable +from typing import Literal from typing import Optional from typing import Protocol from typing import Set @@ -45,7 +46,6 @@ from .path_registry import PathRegistry from .. import exc as sa_exc from .. import inspection from .. import util -from ..util.typing import Literal from ..util.typing import TupleAny from ..util.typing import Unpack diff --git a/lib/sqlalchemy/orm/state_changes.py b/lib/sqlalchemy/orm/state_changes.py index a79874e1c7..c39fbaf90a 100644 --- a/lib/sqlalchemy/orm/state_changes.py +++ b/lib/sqlalchemy/orm/state_changes.py @@ -15,6 +15,7 @@ from typing import Any from typing import Callable from typing import cast from typing import Iterator +from typing import Literal from typing import NoReturn from typing import Optional from typing import Tuple @@ -23,7 +24,6 @@ from typing import Union from .. import exc as sa_exc from .. import util -from ..util.typing import Literal _F = TypeVar("_F", bound=Callable[..., Any]) diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 8e67973e4b..392ba671e9 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -16,6 +16,7 @@ import collections import itertools from typing import Any from typing import Dict +from typing import Literal from typing import Optional from typing import Tuple from typing import TYPE_CHECKING @@ -59,7 +60,6 @@ from ..sql import util as sql_util from ..sql import visitors from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL from ..sql.selectable import Select -from ..util.typing import Literal if TYPE_CHECKING: from .mapper import Mapper diff --git a/lib/sqlalchemy/orm/strategy_options.py b/lib/sqlalchemy/orm/strategy_options.py index d41eaec0b2..96d2024e52 100644 --- a/lib/sqlalchemy/orm/strategy_options.py +++ b/lib/sqlalchemy/orm/strategy_options.py @@ -17,6 +17,7 @@ from typing import cast from typing import Dict from typing import Final from typing import Iterable +from typing import Literal from typing import Optional from typing import overload from typing import Sequence @@ -52,7 +53,6 @@ from ..sql import roles from ..sql import traversals from ..sql import visitors from ..sql.base import _generative -from ..util.typing import Literal from ..util.typing import Self _RELATIONSHIP_TOKEN: Final[Literal["relationship"]] = "relationship" diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py index eb8472993a..29f77bca8f 100644 --- a/lib/sqlalchemy/orm/util.py +++ b/lib/sqlalchemy/orm/util.py @@ -20,9 +20,11 @@ from typing import cast from typing import Dict from typing import FrozenSet from typing import Generic +from typing import get_origin from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import Match from typing import Optional from typing import Protocol @@ -90,9 +92,7 @@ from ..util.langhelpers import MemoizedSlots from ..util.typing import de_stringify_annotation as _de_stringify_annotation from ..util.typing import eval_name_only as _eval_name_only from ..util.typing import fixup_container_fwd_refs -from ..util.typing import get_origin from ..util.typing import is_origin_of_cls -from ..util.typing import Literal from ..util.typing import TupleAny from ..util.typing import Unpack diff --git a/lib/sqlalchemy/orm/writeonly.py b/lib/sqlalchemy/orm/writeonly.py index 347d0d92da..1343a74bfe 100644 --- a/lib/sqlalchemy/orm/writeonly.py +++ b/lib/sqlalchemy/orm/writeonly.py @@ -25,6 +25,7 @@ from typing import Generic from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import NoReturn from typing import Optional from typing import overload @@ -55,7 +56,6 @@ from ..sql import update from ..sql.dml import Delete from ..sql.dml import Insert from ..sql.dml import Update -from ..util.typing import Literal if TYPE_CHECKING: from . import QueryableAttribute diff --git a/lib/sqlalchemy/pool/base.py b/lib/sqlalchemy/pool/base.py index e25e000f01..5e73ccd9c9 100644 --- a/lib/sqlalchemy/pool/base.py +++ b/lib/sqlalchemy/pool/base.py @@ -22,6 +22,7 @@ from typing import cast from typing import Deque from typing import Dict from typing import List +from typing import Literal from typing import Optional from typing import Protocol from typing import Tuple @@ -33,7 +34,6 @@ from .. import event from .. import exc from .. import log from .. import util -from ..util.typing import Literal if TYPE_CHECKING: from ..engine.interfaces import DBAPIConnection diff --git a/lib/sqlalchemy/pool/impl.py b/lib/sqlalchemy/pool/impl.py index d57a2dee46..af39bba170 100644 --- a/lib/sqlalchemy/pool/impl.py +++ b/lib/sqlalchemy/pool/impl.py @@ -15,6 +15,7 @@ import typing from typing import Any from typing import cast from typing import List +from typing import Literal from typing import Optional from typing import Set from typing import Type @@ -34,7 +35,6 @@ from .. import exc from .. import util from ..util import chop_traceback from ..util import queue as sqla_queue -from ..util.typing import Literal if typing.TYPE_CHECKING: from ..engine.interfaces import DBAPIConnection diff --git a/lib/sqlalchemy/sql/_elements_constructors.py b/lib/sqlalchemy/sql/_elements_constructors.py index 7fe4abb545..8e3a718221 100644 --- a/lib/sqlalchemy/sql/_elements_constructors.py +++ b/lib/sqlalchemy/sql/_elements_constructors.py @@ -10,6 +10,7 @@ from __future__ import annotations import typing from typing import Any from typing import Callable +from typing import Literal from typing import Mapping from typing import Optional from typing import overload @@ -46,7 +47,6 @@ from .elements import TypeCoerce from .elements import UnaryExpression from .elements import WithinGroup from .functions import FunctionElement -from ..util.typing import Literal if typing.TYPE_CHECKING: from ._typing import _ByArgument diff --git a/lib/sqlalchemy/sql/_orm_types.py b/lib/sqlalchemy/sql/_orm_types.py index c37d805ef3..142e9f501a 100644 --- a/lib/sqlalchemy/sql/_orm_types.py +++ b/lib/sqlalchemy/sql/_orm_types.py @@ -14,7 +14,7 @@ are meaningful to the ORM. from __future__ import annotations -from ..util.typing import Literal +from typing import Literal SynchronizeSessionArgument = Literal[False, "auto", "evaluate", "fetch"] DMLStrategyArgument = Literal["bulk", "raw", "orm", "auto"] diff --git a/lib/sqlalchemy/sql/_typing.py b/lib/sqlalchemy/sql/_typing.py index 71f54a63f1..b4af798dbd 100644 --- a/lib/sqlalchemy/sql/_typing.py +++ b/lib/sqlalchemy/sql/_typing.py @@ -13,6 +13,7 @@ from typing import Callable from typing import Dict from typing import Generic from typing import Iterable +from typing import Literal from typing import Mapping from typing import NoReturn from typing import Optional @@ -21,6 +22,7 @@ from typing import Protocol from typing import Set from typing import Type from typing import TYPE_CHECKING +from typing import TypeAlias from typing import TypeVar from typing import Union @@ -28,9 +30,7 @@ from . import roles from .. import exc from .. import util from ..inspection import Inspectable -from ..util.typing import Literal from ..util.typing import TupleAny -from ..util.typing import TypeAlias from ..util.typing import TypeVarTuple from ..util.typing import Unpack @@ -40,6 +40,7 @@ if TYPE_CHECKING: from datetime import time from datetime import timedelta from decimal import Decimal + from typing import TypeGuard from uuid import UUID from .base import Executable @@ -76,7 +77,6 @@ if TYPE_CHECKING: from ..engine import Dialect from ..engine import Engine from ..engine.mock import MockConnection - from ..util.typing import TypeGuard _T = TypeVar("_T", bound=Any) _T_co = TypeVar("_T_co", bound=Any, covariant=True) diff --git a/lib/sqlalchemy/sql/_util_cy.py b/lib/sqlalchemy/sql/_util_cy.py index c8d303d359..8d4ef542b9 100644 --- a/lib/sqlalchemy/sql/_util_cy.py +++ b/lib/sqlalchemy/sql/_util_cy.py @@ -8,12 +8,11 @@ from __future__ import annotations from typing import Dict +from typing import Literal from typing import Tuple from typing import TYPE_CHECKING from typing import Union -from ..util.typing import Literal - if TYPE_CHECKING: from .cache_key import CacheConst diff --git a/lib/sqlalchemy/sql/annotation.py b/lib/sqlalchemy/sql/annotation.py index 0fb2390c11..74b0467ebd 100644 --- a/lib/sqlalchemy/sql/annotation.py +++ b/lib/sqlalchemy/sql/annotation.py @@ -24,6 +24,7 @@ from typing import Callable from typing import cast from typing import Dict from typing import FrozenSet +from typing import Literal from typing import Mapping from typing import Optional from typing import overload @@ -39,7 +40,6 @@ from .visitors import anon_map from .visitors import ExternallyTraversible from .visitors import InternalTraversal from .. import util -from ..util.typing import Literal from ..util.typing import Self if TYPE_CHECKING: diff --git a/lib/sqlalchemy/sql/base.py b/lib/sqlalchemy/sql/base.py index b016c291b0..7a5a40d846 100644 --- a/lib/sqlalchemy/sql/base.py +++ b/lib/sqlalchemy/sql/base.py @@ -40,6 +40,7 @@ from typing import Set from typing import Tuple from typing import Type from typing import TYPE_CHECKING +from typing import TypeGuard from typing import TypeVar from typing import Union @@ -58,7 +59,6 @@ from .. import util from ..util import HasMemoized as HasMemoized from ..util import hybridmethod from ..util.typing import Self -from ..util.typing import TypeGuard from ..util.typing import TypeVarTuple from ..util.typing import Unpack diff --git a/lib/sqlalchemy/sql/cache_key.py b/lib/sqlalchemy/sql/cache_key.py index c8fa205691..f44ca26886 100644 --- a/lib/sqlalchemy/sql/cache_key.py +++ b/lib/sqlalchemy/sql/cache_key.py @@ -16,6 +16,7 @@ from typing import Dict from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import MutableMapping from typing import NamedTuple from typing import Optional @@ -32,7 +33,6 @@ from .visitors import prefix_anon_map from .. import util from ..inspection import inspect from ..util import HasMemoized -from ..util.typing import Literal if typing.TYPE_CHECKING: from .elements import BindParameter diff --git a/lib/sqlalchemy/sql/coercions.py b/lib/sqlalchemy/sql/coercions.py index 5cb74948bd..c967ab0c98 100644 --- a/lib/sqlalchemy/sql/coercions.py +++ b/lib/sqlalchemy/sql/coercions.py @@ -19,6 +19,7 @@ from typing import Dict from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import NoReturn from typing import Optional from typing import overload @@ -39,7 +40,6 @@ from .visitors import Visitable from .. import exc from .. import inspection from .. import util -from ..util.typing import Literal if typing.TYPE_CHECKING: # elements lambdas schema selectable are set by __init__ diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index eb457dd410..bf11125369 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -44,6 +44,7 @@ from typing import FrozenSet from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import Mapping from typing import MutableMapping from typing import NamedTuple @@ -83,7 +84,6 @@ from .visitors import prefix_anon_map from .. import exc from .. import util from ..util import FastIntFlag -from ..util.typing import Literal from ..util.typing import Self from ..util.typing import TupleAny from ..util.typing import Unpack diff --git a/lib/sqlalchemy/sql/crud.py b/lib/sqlalchemy/sql/crud.py index 51bede81fd..bb63a41e72 100644 --- a/lib/sqlalchemy/sql/crud.py +++ b/lib/sqlalchemy/sql/crud.py @@ -21,6 +21,7 @@ from typing import cast from typing import Dict from typing import Iterable from typing import List +from typing import Literal from typing import MutableMapping from typing import NamedTuple from typing import Optional @@ -44,7 +45,6 @@ from .selectable import Select from .selectable import TableClause from .. import exc from .. import util -from ..util.typing import Literal if TYPE_CHECKING: from .compiler import _BindNameForColProtocol diff --git a/lib/sqlalchemy/sql/dml.py b/lib/sqlalchemy/sql/dml.py index 73e61de65d..99d983b88a 100644 --- a/lib/sqlalchemy/sql/dml.py +++ b/lib/sqlalchemy/sql/dml.py @@ -28,6 +28,7 @@ from typing import Set from typing import Tuple from typing import Type from typing import TYPE_CHECKING +from typing import TypeGuard from typing import TypeVar from typing import Union @@ -71,7 +72,6 @@ from .. import exc from .. import util from ..util.typing import Self from ..util.typing import TupleAny -from ..util.typing import TypeGuard from ..util.typing import TypeVarTuple from ..util.typing import Unpack diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index 88e4f68999..6d24d15da2 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -29,9 +29,11 @@ from typing import Generic from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import Mapping from typing import Optional from typing import overload +from typing import ParamSpec from typing import Sequence from typing import Set from typing import Tuple as typing_Tuple @@ -76,8 +78,6 @@ from .. import inspection from .. import util from ..util import HasMemoized_ro_memoized_attribute from ..util import TypingOnly -from ..util.typing import Literal -from ..util.typing import ParamSpec from ..util.typing import Self from ..util.typing import TupleAny from ..util.typing import Unpack diff --git a/lib/sqlalchemy/sql/lambdas.py b/lib/sqlalchemy/sql/lambdas.py index 21c69fed5a..731f7e5932 100644 --- a/lib/sqlalchemy/sql/lambdas.py +++ b/lib/sqlalchemy/sql/lambdas.py @@ -19,6 +19,7 @@ from typing import Any from typing import Callable from typing import cast from typing import List +from typing import Literal from typing import MutableMapping from typing import Optional from typing import Tuple @@ -42,7 +43,6 @@ from .operators import ColumnOperators from .. import exc from .. import inspection from .. import util -from ..util.typing import Literal if TYPE_CHECKING: diff --git a/lib/sqlalchemy/sql/operators.py b/lib/sqlalchemy/sql/operators.py index 7e751e13d0..00f885a084 100644 --- a/lib/sqlalchemy/sql/operators.py +++ b/lib/sqlalchemy/sql/operators.py @@ -41,6 +41,7 @@ from typing import Callable from typing import cast from typing import Dict from typing import Generic +from typing import Literal from typing import Optional from typing import overload from typing import Protocol @@ -53,7 +54,6 @@ from typing import Union from .. import exc from .. import util -from ..util.typing import Literal if typing.TYPE_CHECKING: from ._typing import ColumnExpressionArgument diff --git a/lib/sqlalchemy/sql/roles.py b/lib/sqlalchemy/sql/roles.py index 99f9fc231c..5a23d0d4d9 100644 --- a/lib/sqlalchemy/sql/roles.py +++ b/lib/sqlalchemy/sql/roles.py @@ -8,12 +8,12 @@ from __future__ import annotations from typing import Any from typing import Generic +from typing import Literal from typing import Optional from typing import TYPE_CHECKING from typing import TypeVar from .. import util -from ..util.typing import Literal if TYPE_CHECKING: from ._typing import _PropagateAttrsType diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py index 92ecadb792..4e640e2af4 100644 --- a/lib/sqlalchemy/sql/schema.py +++ b/lib/sqlalchemy/sql/schema.py @@ -44,6 +44,7 @@ from typing import Final from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import Mapping from typing import NoReturn from typing import Optional @@ -54,6 +55,7 @@ from typing import Set from typing import Tuple from typing import TYPE_CHECKING from typing import TypedDict +from typing import TypeGuard from typing import TypeVar from typing import Union @@ -86,9 +88,7 @@ from .. import exc from .. import inspection from .. import util from ..util import HasMemoized -from ..util.typing import Literal from ..util.typing import Self -from ..util.typing import TypeGuard if typing.TYPE_CHECKING: from ._typing import _AutoIncrementType diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py index c19d989fbd..1ec6307c96 100644 --- a/lib/sqlalchemy/sql/selectable.py +++ b/lib/sqlalchemy/sql/selectable.py @@ -26,6 +26,7 @@ from typing import Generic from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import NamedTuple from typing import NoReturn from typing import Optional @@ -103,7 +104,6 @@ from .. import exc from .. import util from ..util import HasMemoized_ro_memoized_attribute from ..util import warn_deprecated -from ..util.typing import Literal from ..util.typing import Self from ..util.typing import TupleAny from ..util.typing import Unpack diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index 3053dd857b..81a2bbf67d 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -21,8 +21,10 @@ from typing import Callable from typing import cast from typing import Dict from typing import Generic +from typing import get_args from typing import Iterable from typing import List +from typing import Literal from typing import Mapping from typing import Optional from typing import overload @@ -62,10 +64,8 @@ from .. import util from ..engine import processors from ..util import langhelpers from ..util import OrderedDict -from ..util.typing import get_args from ..util.typing import is_literal from ..util.typing import is_pep695 -from ..util.typing import Literal from ..util.typing import TupleAny if TYPE_CHECKING: diff --git a/lib/sqlalchemy/sql/type_api.py b/lib/sqlalchemy/sql/type_api.py index abfbcb6167..8cfc72c88b 100644 --- a/lib/sqlalchemy/sql/type_api.py +++ b/lib/sqlalchemy/sql/type_api.py @@ -26,6 +26,7 @@ from typing import Tuple from typing import Type from typing import TYPE_CHECKING from typing import TypedDict +from typing import TypeGuard from typing import TypeVar from typing import Union @@ -38,7 +39,6 @@ from .. import exc from .. import util from ..util.typing import Self from ..util.typing import TypeAliasType -from ..util.typing import TypeGuard # these are back-assigned by sqltypes. if typing.TYPE_CHECKING: diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py index 128fc6d345..868927a3e7 100644 --- a/lib/sqlalchemy/sql/util.py +++ b/lib/sqlalchemy/sql/util.py @@ -22,6 +22,7 @@ from typing import Dict from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import Optional from typing import overload from typing import Protocol @@ -67,7 +68,6 @@ from .selectable import TableClause from .visitors import _ET from .. import exc from .. import util -from ..util.typing import Literal from ..util.typing import Unpack if typing.TYPE_CHECKING: diff --git a/lib/sqlalchemy/sql/visitors.py b/lib/sqlalchemy/sql/visitors.py index a5cf585ba4..1cd7097f2d 100644 --- a/lib/sqlalchemy/sql/visitors.py +++ b/lib/sqlalchemy/sql/visitors.py @@ -22,6 +22,7 @@ from typing import Dict from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import Mapping from typing import Optional from typing import overload @@ -37,7 +38,6 @@ from ._util_cy import prefix_anon_map as prefix_anon_map # noqa: F401 from .. import exc from .. import util from ..util import langhelpers -from ..util.typing import Literal from ..util.typing import Self if TYPE_CHECKING: diff --git a/lib/sqlalchemy/testing/engines.py b/lib/sqlalchemy/testing/engines.py index fc51e7d873..fdb6d18b13 100644 --- a/lib/sqlalchemy/testing/engines.py +++ b/lib/sqlalchemy/testing/engines.py @@ -14,6 +14,7 @@ import re import typing from typing import Any from typing import Dict +from typing import Literal from typing import Optional import warnings import weakref @@ -24,7 +25,6 @@ from .util import gc_collect from .. import event from .. import pool from ..util import await_ -from ..util.typing import Literal if typing.TYPE_CHECKING: diff --git a/lib/sqlalchemy/testing/fixtures/mypy.py b/lib/sqlalchemy/testing/fixtures/mypy.py index 4b43225789..b1d2ee0e81 100644 --- a/lib/sqlalchemy/testing/fixtures/mypy.py +++ b/lib/sqlalchemy/testing/fixtures/mypy.py @@ -19,7 +19,6 @@ import tempfile from .base import TestBase from .. import config from ..assertions import eq_ -from ... import util try: from mypy import version @@ -206,8 +205,8 @@ class MypyTest(TestBase): is_mypy = is_re = True expected_msg = f'Revealed type is "{expected_msg}"' - if mypy_14 and util.py310: - # use_or_syntax, py310 and above + if mypy_14: + # use_or_syntax # https://github.com/python/mypy/blob/304997bfb85200fb521ac727ee0ce3e6085e5278/mypy/options.py#L368 # noqa: E501 expected_msg = re.sub( r"Optional\[(.*?)\]", diff --git a/lib/sqlalchemy/testing/requirements.py b/lib/sqlalchemy/testing/requirements.py index 3d109be308..6c1d3918a2 100644 --- a/lib/sqlalchemy/testing/requirements.py +++ b/lib/sqlalchemy/testing/requirements.py @@ -1590,12 +1590,6 @@ class SuiteRequirements(Requirements): return exclusions.only_if(check) - @property - def python310(self): - return exclusions.only_if( - lambda: util.py310, "Python 3.10 or above required" - ) - @property def python311(self): return exclusions.only_if( diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py index a2110c4ec5..167d135adb 100644 --- a/lib/sqlalchemy/util/__init__.py +++ b/lib/sqlalchemy/util/__init__.py @@ -46,7 +46,6 @@ from ._collections import UniqueAppender as UniqueAppender from ._collections import update_copy as update_copy from ._collections import WeakPopulateDict as WeakPopulateDict from ._collections import WeakSequence as WeakSequence -from .compat import anext_ as anext_ from .compat import arm as arm from .compat import b as b from .compat import b64decode as b64decode @@ -61,7 +60,6 @@ from .compat import inspect_getfullargspec as inspect_getfullargspec from .compat import is64bit as is64bit from .compat import local_dataclass_fields as local_dataclass_fields from .compat import osx as osx -from .compat import py310 as py310 from .compat import py311 as py311 from .compat import py312 as py312 from .compat import py313 as py313 @@ -130,7 +128,6 @@ from .langhelpers import ( monkeypatch_proxied_specials as monkeypatch_proxied_specials, ) from .langhelpers import non_memoized_property as non_memoized_property -from .langhelpers import NoneType as NoneType from .langhelpers import only_once as only_once from .langhelpers import ( parse_user_argument_for_enum as parse_user_argument_for_enum, diff --git a/lib/sqlalchemy/util/_collections.py b/lib/sqlalchemy/util/_collections.py index 36ca6a56a9..8940f38163 100644 --- a/lib/sqlalchemy/util/_collections.py +++ b/lib/sqlalchemy/util/_collections.py @@ -23,6 +23,7 @@ from typing import Generic from typing import Iterable from typing import Iterator from typing import List +from typing import Literal from typing import Mapping from typing import NoReturn from typing import Optional @@ -43,7 +44,6 @@ from ._immutabledict_cy import immutabledict as immutabledict from ._immutabledict_cy import ImmutableDictBase as ImmutableDictBase from ._immutabledict_cy import ReadOnlyContainer as ReadOnlyContainer from .typing import is_non_string_iterable -from .typing import Literal _T = TypeVar("_T", bound=Any) diff --git a/lib/sqlalchemy/util/compat.py b/lib/sqlalchemy/util/compat.py index 7dd7775468..a62bb6a257 100644 --- a/lib/sqlalchemy/util/compat.py +++ b/lib/sqlalchemy/util/compat.py @@ -36,7 +36,6 @@ py314 = sys.version_info >= (3, 14) py313 = sys.version_info >= (3, 13) py312 = sys.version_info >= (3, 12) py311 = sys.version_info >= (3, 11) -py310 = sys.version_info >= (3, 10) pypy = platform.python_implementation() == "PyPy" cpython = platform.python_implementation() == "CPython" @@ -104,28 +103,6 @@ def md5_not_for_security() -> Any: return hashlib.md5(usedforsecurity=False) -if py310: - anext_ = anext -else: - _NOT_PROVIDED = object() - from collections.abc import AsyncIterator - - async def anext_(async_iterator, default=_NOT_PROVIDED): - """vendored from https://github.com/python/cpython/pull/8895""" - - if not isinstance(async_iterator, AsyncIterator): - raise TypeError( - f"anext expected an AsyncIterator, got {type(async_iterator)}" - ) - anxt = type(async_iterator).__anext__ - try: - return await anxt(async_iterator) - except StopAsyncIteration: - if default is _NOT_PROVIDED: - raise - return default - - def importlib_metadata_get(group): ep = importlib_metadata.entry_points() if typing.TYPE_CHECKING or hasattr(ep, "select"): diff --git a/lib/sqlalchemy/util/concurrency.py b/lib/sqlalchemy/util/concurrency.py index da758e5dce..d5250f7bbb 100644 --- a/lib/sqlalchemy/util/concurrency.py +++ b/lib/sqlalchemy/util/concurrency.py @@ -14,16 +14,16 @@ from typing import Any from typing import Awaitable from typing import Callable from typing import Coroutine +from typing import Literal from typing import NoReturn from typing import TYPE_CHECKING +from typing import TypeGuard from typing import TypeVar from typing import Union from .compat import py311 from .langhelpers import memoized_property -from .typing import Literal from .typing import Self -from .typing import TypeGuard from .. import exc _T = TypeVar("_T") diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py index f82ab5cde8..528723494c 100644 --- a/lib/sqlalchemy/util/langhelpers.py +++ b/lib/sqlalchemy/util/langhelpers.py @@ -34,6 +34,7 @@ from typing import FrozenSet from typing import Generic from typing import Iterator from typing import List +from typing import Literal from typing import Mapping from typing import NoReturn from typing import Optional @@ -49,7 +50,6 @@ import warnings from . import _collections from . import compat -from .typing import Literal from .. import exc _T = TypeVar("_T") @@ -136,24 +136,10 @@ if compat.py314: # away in future python as a separate mode return _vendored_get_annotations(obj, format=Format.FORWARDREF) -elif compat.py310: - - def get_annotations(obj: Any) -> Mapping[str, Any]: - return inspect.get_annotations(obj) - else: def get_annotations(obj: Any) -> Mapping[str, Any]: - # https://docs.python.org/3/howto/annotations.html#annotations-howto - if isinstance(obj, type): - ann = obj.__dict__.get("__annotations__", None) - else: - ann = obj.__annotations__ - - if ann is None: - return _collections.EMPTY_DICT - else: - return cast("Mapping[str, Any]", ann) + return inspect.get_annotations(obj) def md5_hex(x: Any) -> str: @@ -2003,9 +1989,6 @@ def chop_traceback( return tb[start : end + 1] -NoneType = type(None) - - def attrsetter(attrname): code = "def set(obj, value): obj.%s = value" % attrname env = locals().copy() diff --git a/lib/sqlalchemy/util/typing.py b/lib/sqlalchemy/util/typing.py index 7a59dd536e..439bbb85e7 100644 --- a/lib/sqlalchemy/util/typing.py +++ b/lib/sqlalchemy/util/typing.py @@ -13,13 +13,17 @@ from collections import deque import collections.abc as collections_abc import re import sys +from types import NoneType import typing from typing import Any from typing import Callable from typing import Dict from typing import ForwardRef from typing import Generic +from typing import get_args +from typing import get_origin from typing import Iterable +from typing import Literal from typing import Mapping from typing import NewType from typing import NoReturn @@ -30,6 +34,7 @@ from typing import Set from typing import Tuple from typing import Type from typing import TYPE_CHECKING +from typing import TypeGuard from typing import TypeVar from typing import Union @@ -38,20 +43,10 @@ import typing_extensions from . import compat if True: # zimports removes the tailing comments - from typing_extensions import Annotated as Annotated # 3.9 - from typing_extensions import Concatenate as Concatenate # 3.10 from typing_extensions import ( dataclass_transform as dataclass_transform, # 3.11, ) - from typing_extensions import get_args as get_args # 3.10 - from typing_extensions import get_origin as get_origin # 3.10 - from typing_extensions import ( - Literal as Literal, - ) # 3.8 but has bugs before 3.10 from typing_extensions import NotRequired as NotRequired # 3.11 - from typing_extensions import ParamSpec as ParamSpec # 3.10 - from typing_extensions import TypeAlias as TypeAlias # 3.10 - from typing_extensions import TypeGuard as TypeGuard # 3.10 from typing_extensions import TypeVarTuple as TypeVarTuple # 3.11 from typing_extensions import Self as Self # 3.11 from typing_extensions import TypeAliasType as TypeAliasType # 3.12 @@ -70,14 +65,6 @@ _VT_co = TypeVar("_VT_co", covariant=True) TupleAny = Tuple[Any, ...] -if compat.py310: - # why they took until py310 to put this in stdlib is beyond me, - # I've been wanting it since py27 - from types import NoneType as NoneType -else: - NoneType = type(None) # type: ignore - - def is_fwd_none(typ: Any) -> bool: return isinstance(typ, ForwardRef) and typ.__forward_arg__ == "None" @@ -344,10 +331,7 @@ def is_literal(type_: Any) -> bool: def is_newtype(type_: Optional[_AnnotationScanType]) -> TypeGuard[NewType]: - return hasattr(type_, "__supertype__") - # doesn't work in 3.9, 3.8, 3.7 as it passes a closure, not an - # object instance - # isinstance(type, type_instances.NewType) + return isinstance(type_, _type_tuples.NewType) def is_generic(type_: _AnnotationScanType) -> TypeGuard[GenericProtocol[Any]]: @@ -598,22 +582,11 @@ def is_origin_of( if origin is None: return False - return _get_type_name(origin) in names and ( + return origin.__name__ in names and ( module is None or origin.__module__.startswith(module) ) -def _get_type_name(type_: Type[Any]) -> str: - if compat.py310: - return type_.__name__ - else: - typ_name = getattr(type_, "__name__", None) - if typ_name is None: - typ_name = getattr(type_, "_name", None) - - return typ_name # type: ignore - - class DescriptorProto(Protocol): def __get__(self, instance: object, owner: Any) -> Any: ... diff --git a/pyproject.toml b/pyproject.toml index 9010569134..2bd90fa25d 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.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -29,7 +28,7 @@ classifiers = [ "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Database :: Front-Ends", ] -requires-python = ">=3.9" +requires-python = ">=3.10" dependencies = [ "typing-extensions >= 4.6.0", ] @@ -118,7 +117,7 @@ tag-build = "dev" [tool.black] line-length = 79 -target-version = ['py39'] +target-version = ['py310'] [tool.zimports] diff --git a/test/base/test_tutorials.py b/test/base/test_tutorials.py index d86322e12e..672606dfea 100644 --- a/test/base/test_tutorials.py +++ b/test/base/test_tutorials.py @@ -14,7 +14,7 @@ from sqlalchemy.testing import skip_test class DocTest(fixtures.TestBase): - __requires__ = ("python310", "insert_returning", "insertmanyvalues") + __requires__ = ("insert_returning", "insertmanyvalues") __only_on__ = "sqlite+pysqlite" def _setup_logger(self): diff --git a/test/base/test_typing_utils.py b/test/base/test_typing_utils.py index b1ba3cdee1..c5c5015adb 100644 --- a/test/base/test_typing_utils.py +++ b/test/base/test_typing_utils.py @@ -9,7 +9,6 @@ from sqlalchemy.testing import fixtures from sqlalchemy.testing import requires from sqlalchemy.testing.assertions import eq_ from sqlalchemy.testing.assertions import is_ -from sqlalchemy.util import py310 from sqlalchemy.util import py312 from sqlalchemy.util import py314 from sqlalchemy.util import typing as sa_typing @@ -18,9 +17,7 @@ TV = typing.TypeVar("TV") def union_types(): - res = [typing.Union[int, str]] - if py310: - res.append(int | str) + res = [typing.Union[int, str], int | str] return res @@ -29,18 +26,17 @@ def null_union_types(): typing.Optional[typing.Union[int, str]], typing.Union[int, str, None], typing.Union[int, str, "None"], + int | str | None, + typing.Optional[int | str], + typing.Union[int, str] | None, + typing.Optional[int] | str, ] - if py310: - res.append(int | str | None) - res.append(typing.Optional[int | str]) - res.append(typing.Union[int, str] | None) - res.append(typing.Optional[int] | str) return res def generic_unions(): res = union_types() + null_union_types() - if py310 and not py314: + if not py314: # for py310 through py313, remove new-style unions `int | str` that # are not generic new_ut = type(int | str) @@ -190,12 +186,10 @@ def new_types(): return [NT_str, NT_null, NT_union] -A_str = typing_extensions.Annotated[str, "meta"] -A_null_str = typing_extensions.Annotated[ - typing.Union[str, None], "other_meta", "null" -] -A_union = typing_extensions.Annotated[typing.Union[str, int], "other_meta"] -A_null_union = typing_extensions.Annotated[ +A_str = typing.Annotated[str, "meta"] +A_null_str = typing.Annotated[typing.Union[str, None], "other_meta", "null"] +A_union = typing.Annotated[typing.Union[str, int], "other_meta"] +A_null_union = typing.Annotated[ typing.Union[str, int, None], "other_meta", "null" ] @@ -283,8 +277,7 @@ class TestTyping(fixtures.TestBase): eq_(sa_typing.is_pep593(str), False) eq_(sa_typing.is_pep593(None), False) eq_(sa_typing.is_pep593(typing_extensions.Annotated[int, "a"]), True) - if py310: - eq_(sa_typing.is_pep593(typing.Annotated[int, "a"]), True) + eq_(sa_typing.is_pep593(typing.Annotated[int, "a"]), True) for t in annotated_l(): eq_(sa_typing.is_pep593(t), True) diff --git a/test/ext/asyncio/test_engine_py3k.py b/test/ext/asyncio/test_engine_py3k.py index 901b47f42f..a8d2e2ce3c 100644 --- a/test/ext/asyncio/test_engine_py3k.py +++ b/test/ext/asyncio/test_engine_py3k.py @@ -754,7 +754,6 @@ class AsyncEngineTest(EngineFixture): with expect_raises(exc.TimeoutError): await engine.connect() - @testing.requires.python310 @async_test async def test_engine_aclose(self, async_engine): users = self.tables.users diff --git a/test/ext/asyncio/test_session_py3k.py b/test/ext/asyncio/test_session_py3k.py index 5f9bf2e089..3ad10337b9 100644 --- a/test/ext/asyncio/test_session_py3k.py +++ b/test/ext/asyncio/test_session_py3k.py @@ -197,7 +197,6 @@ class AsyncSessionQueryTest(AsyncFixture): result = await async_session.scalar(stmt) eq_(result, 7) - @testing.requires.python310 @async_test async def test_session_aclose(self, async_session): User = self.classes.User diff --git a/test/ext/test_associationproxy.py b/test/ext/test_associationproxy.py index 1aca0c97e2..2c38fbc31c 100644 --- a/test/ext/test_associationproxy.py +++ b/test/ext/test_associationproxy.py @@ -3912,7 +3912,7 @@ class DeclOrmForms(fixtures.TestBase): {}, {"repr": False}, {"repr": True}, - ({"kw_only": True}, testing.requires.python310), + {"kw_only": True}, {"init": False}, {"default_factory": True}, argnames="field_kw", diff --git a/test/orm/declarative/test_dc_transforms.py b/test/orm/declarative/test_dc_transforms.py index 3bc260077c..6d26c58f02 100644 --- a/test/orm/declarative/test_dc_transforms.py +++ b/test/orm/declarative/test_dc_transforms.py @@ -4,6 +4,7 @@ from dataclasses import InitVar import functools import inspect as pyinspect from itertools import product +from typing import Annotated from typing import Any from typing import ClassVar from typing import Dict @@ -15,8 +16,6 @@ from typing import Type from typing import TypeVar from unittest import mock -from typing_extensions import Annotated - from sqlalchemy import BigInteger from sqlalchemy import Column from sqlalchemy import exc @@ -60,7 +59,6 @@ from sqlalchemy.testing import is_false from sqlalchemy.testing import is_true from sqlalchemy.testing import ne_ from sqlalchemy.testing import Variation -from sqlalchemy.util import compat def _dataclass_mixin_warning(clsname, attrnames): @@ -721,7 +719,6 @@ class DCTransformsTest(AssertsCompiledSQL, fixtures.TestBase): a2 = A(id=1, data="foo") eq_(a1, a2) - @testing.requires.python310 def test_kw_only_attribute(self, dc_decl_base: Type[MappedAsDataclass]): class A(dc_decl_base): __tablename__ = "a" @@ -754,7 +751,6 @@ class DCTransformsTest(AssertsCompiledSQL, fixtures.TestBase): a.data = "y" ne_(hash(a), a_hash1) - @testing.requires.python310 def test_kw_only_dataclass_constant( self, dc_decl_base: Type[MappedAsDataclass] ): @@ -1470,9 +1466,15 @@ class DataclassesForNonMappedClassesTest(fixtures.TestBase): class DataclassArgsTest(fixtures.TestBase): - dc_arg_names = ("init", "repr", "eq", "order", "unsafe_hash") - if compat.py310: - dc_arg_names += ("match_args", "kw_only") + dc_arg_names = ( + "init", + "repr", + "eq", + "order", + "unsafe_hash", + "match_args", + "kw_only", + ) @testing.fixture(params=product(dc_arg_names, (True, False))) def dc_argument_fixture(self, request: Any, registry: _RegistryType): @@ -1488,9 +1490,9 @@ class DataclassArgsTest(fixtures.TestBase): "eq": True, "order": False, "unsafe_hash": False, + "match_args": True, + "kw_only": False, } - if compat.py310: - default |= {"match_args": True, "kw_only": False} to_apply = {k: v for k, v in args.items() if v} effective = {**default, **to_apply} return to_apply, effective @@ -1779,9 +1781,9 @@ class DataclassArgsTest(fixtures.TestBase): "eq": True, "order": True, "unsafe_hash": False, + "match_args": True, + "kw_only": False, } - if compat.py310: - effective |= {"match_args": True, "kw_only": False} self._assert_cls(A, effective) def test_dc_base_unsupported_argument(self, registry: _RegistryType): diff --git a/test/orm/declarative/test_dc_transforms_future_anno_sync.py b/test/orm/declarative/test_dc_transforms_future_anno_sync.py index 035abc19c1..a5e2c53c45 100644 --- a/test/orm/declarative/test_dc_transforms_future_anno_sync.py +++ b/test/orm/declarative/test_dc_transforms_future_anno_sync.py @@ -13,6 +13,7 @@ from dataclasses import InitVar import functools import inspect as pyinspect from itertools import product +from typing import Annotated from typing import Any from typing import ClassVar from typing import Dict @@ -24,8 +25,6 @@ from typing import Type from typing import TypeVar from unittest import mock -from typing_extensions import Annotated - from sqlalchemy import BigInteger from sqlalchemy import Column from sqlalchemy import exc @@ -69,7 +68,6 @@ from sqlalchemy.testing import is_false from sqlalchemy.testing import is_true from sqlalchemy.testing import ne_ from sqlalchemy.testing import Variation -from sqlalchemy.util import compat def _dataclass_mixin_warning(clsname, attrnames): @@ -734,7 +732,6 @@ class DCTransformsTest(AssertsCompiledSQL, fixtures.TestBase): a2 = A(id=1, data="foo") eq_(a1, a2) - @testing.requires.python310 def test_kw_only_attribute(self, dc_decl_base: Type[MappedAsDataclass]): class A(dc_decl_base): __tablename__ = "a" @@ -767,7 +764,6 @@ class DCTransformsTest(AssertsCompiledSQL, fixtures.TestBase): a.data = "y" ne_(hash(a), a_hash1) - @testing.requires.python310 def test_kw_only_dataclass_constant( self, dc_decl_base: Type[MappedAsDataclass] ): @@ -1489,9 +1485,15 @@ class DataclassesForNonMappedClassesTest(fixtures.TestBase): class DataclassArgsTest(fixtures.TestBase): - dc_arg_names = ("init", "repr", "eq", "order", "unsafe_hash") - if compat.py310: - dc_arg_names += ("match_args", "kw_only") + dc_arg_names = ( + "init", + "repr", + "eq", + "order", + "unsafe_hash", + "match_args", + "kw_only", + ) @testing.fixture(params=product(dc_arg_names, (True, False))) def dc_argument_fixture(self, request: Any, registry: _RegistryType): @@ -1507,9 +1509,9 @@ class DataclassArgsTest(fixtures.TestBase): "eq": True, "order": False, "unsafe_hash": False, + "match_args": True, + "kw_only": False, } - if compat.py310: - default |= {"match_args": True, "kw_only": False} to_apply = {k: v for k, v in args.items() if v} effective = {**default, **to_apply} return to_apply, effective @@ -1798,9 +1800,9 @@ class DataclassArgsTest(fixtures.TestBase): "eq": True, "order": True, "unsafe_hash": False, + "match_args": True, + "kw_only": False, } - if compat.py310: - effective |= {"match_args": True, "kw_only": False} self._assert_cls(A, effective) def test_dc_base_unsupported_argument(self, registry: _RegistryType): diff --git a/test/orm/declarative/test_mixin.py b/test/orm/declarative/test_mixin.py index 42745e4669..0ec0cdbe62 100644 --- a/test/orm/declarative/test_mixin.py +++ b/test/orm/declarative/test_mixin.py @@ -1,6 +1,5 @@ from operator import is_not - -from typing_extensions import Annotated +from typing import Annotated import sqlalchemy as sa from sqlalchemy import ForeignKey diff --git a/test/orm/declarative/test_tm_future_annotations_sync.py b/test/orm/declarative/test_tm_future_annotations_sync.py index ac343c2315..0ab310277a 100644 --- a/test/orm/declarative/test_tm_future_annotations_sync.py +++ b/test/orm/declarative/test_tm_future_annotations_sync.py @@ -15,26 +15,27 @@ import enum import inspect as _py_inspect import re import typing +from typing import Annotated from typing import Any from typing import cast from typing import ClassVar from typing import Dict from typing import Generic +from typing import get_args as get_args from typing import List +from typing import Literal as Literal from typing import NewType from typing import Optional from typing import Set from typing import Type from typing import TYPE_CHECKING +from typing import TypeAlias as TypeAlias from typing import TypedDict from typing import TypeVar from typing import Union import uuid import typing_extensions -from typing_extensions import get_args as get_args -from typing_extensions import Literal as Literal -from typing_extensions import TypeAlias as TypeAlias from typing_extensions import TypeAliasType from sqlalchemy import BIGINT @@ -102,8 +103,6 @@ from sqlalchemy.testing import requires from sqlalchemy.testing import Variation from sqlalchemy.testing.assertions import ne_ from sqlalchemy.testing.fixtures import fixture_session -from sqlalchemy.util import compat -from sqlalchemy.util.typing import Annotated TV = typing.TypeVar("TV") @@ -128,15 +127,14 @@ _JsonPrimitive: TypeAlias = Union[str, int, float, bool, None] _JsonObject: TypeAlias = Dict[str, "_Json"] _JsonArray: TypeAlias = List["_Json"] _Json: TypeAlias = Union[_JsonObject, _JsonArray, _JsonPrimitive] +_JsonPrimitivePep604: TypeAlias = str | int | float | bool | None +_JsonObjectPep604: TypeAlias = dict[str, "_JsonPep604"] +_JsonArrayPep604: TypeAlias = list["_JsonPep604"] +_JsonPep604: TypeAlias = ( + _JsonObjectPep604 | _JsonArrayPep604 | _JsonPrimitivePep604 +) +_JsonPep695 = TypeAliasType("_JsonPep695", _JsonPep604) -if compat.py310: - _JsonPrimitivePep604: TypeAlias = str | int | float | bool | None - _JsonObjectPep604: TypeAlias = dict[str, "_JsonPep604"] - _JsonArrayPep604: TypeAlias = list["_JsonPep604"] - _JsonPep604: TypeAlias = ( - _JsonObjectPep604 | _JsonArrayPep604 | _JsonPrimitivePep604 - ) - _JsonPep695 = TypeAliasType("_JsonPep695", _JsonPep604) TypingTypeAliasType = getattr(typing, "TypeAliasType", TypeAliasType) @@ -152,11 +150,10 @@ _UnionPep695 = TypeAliasType("_UnionPep695", Union[_SomeDict1, _SomeDict2]) strtypalias_keyword = TypeAliasType( "strtypalias_keyword", Annotated[str, mapped_column(info={"hi": "there"})] ) -if compat.py310: - strtypalias_keyword_nested = TypeAliasType( - "strtypalias_keyword_nested", - int | Annotated[str, mapped_column(info={"hi": "there"})], - ) +strtypalias_keyword_nested = TypeAliasType( + "strtypalias_keyword_nested", + int | Annotated[str, mapped_column(info={"hi": "there"})], +) strtypalias_ta: TypeAlias = Annotated[str, mapped_column(info={"hi": "there"})] strtypalias_plain = Annotated[str, mapped_column(info={"hi": "there"})] _Literal695 = TypeAliasType( @@ -633,11 +630,11 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): "argtype", [ "type", - ("column", testing.requires.python310), - ("mapped_column", testing.requires.python310), + "column", + "mapped_column", "column_class", "ref_to_type", - ("ref_to_column", testing.requires.python310), + "ref_to_column", ], ) def test_construct_lhs_sqlalchemy_type(self, decl_base, argtype): @@ -1308,7 +1305,6 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): eq_(col.type.enums, ["a", "b"]) is_(col.type.native_enum, False) - @testing.requires.python310 def test_we_got_all_attrs_test_annotated(self): argnames = _py_inspect.getfullargspec(mapped_column) assert _annotated_names_tested.issuperset(argnames.kwonlyargs), ( @@ -1419,7 +1415,6 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): exc.SADeprecationWarning( "Argument 'kw_only' is a dataclass argument " ), - testing.requires.python310, ), ( "compare", @@ -1427,7 +1422,6 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): exc.SADeprecationWarning( "Argument 'compare' is a dataclass argument " ), - testing.requires.python310, ), ( "default_factory", @@ -1620,17 +1614,10 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): str50 = NewType("str50", str) - if compat.py310: - text = ".*str50" - else: - # NewTypes before 3.10 had a very bad repr - # .new_type at 0x...> - text = ".*NewType.*" - with expect_raises_message( orm_exc.MappedAnnotationError, "Could not locate SQLAlchemy Core type for Python type " - f"{text} inside the 'data_one' attribute Mapped annotation", + ".*str50 inside the 'data_one' attribute Mapped annotation", ): class MyClass(decl_base): @@ -1848,38 +1835,22 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): to_assert.fail() @testing.combinations( - (collections.abc.Sequence, (str,), testing.requires.python310), - (collections.abc.MutableSequence, (str,), testing.requires.python310), - (collections.abc.Mapping, (str, str), testing.requires.python310), - ( - collections.abc.MutableMapping, - (str, str), - testing.requires.python310, - ), - (typing.Mapping, (str, str), testing.requires.python310), - (typing.MutableMapping, (str, str), testing.requires.python310), + (collections.abc.Sequence, (str,)), + (collections.abc.MutableSequence, (str,)), + (collections.abc.Mapping, (str, str)), + (collections.abc.MutableMapping, (str, str)), + (typing.Mapping, (str, str)), + (typing.MutableMapping, (str, str)), (typing.Sequence, (str,)), (typing.MutableSequence, (str,)), - (list, (str,), testing.requires.python310), - ( - List, - (str,), - ), - (dict, (str, str), testing.requires.python310), - ( - Dict, - (str, str), - ), - (list, None, testing.requires.python310), - ( - List, - None, - ), - (dict, None, testing.requires.python310), - ( - Dict, - None, - ), + (list, (str,)), + (List, (str,)), + (dict, (str, str)), + (Dict, (str, str)), + (list, None), + (List, None), + (dict, None), + (Dict, None), id_="sa", argnames="container_typ,args", ) @@ -2166,12 +2137,7 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): @testing.variation( "union", - [ - "union", - ("pep604", requires.python310), - "union_null", - ("pep604_null", requires.python310), - ], + ["union", "pep604", "union_null", "pep604_null"], ) def test_unions(self, union): global UnionType @@ -2227,17 +2193,14 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): float_data: Mapped[float] = mapped_column() decimal_data: Mapped[Decimal] = mapped_column() - if compat.py310: - pep604_data: Mapped[float | Decimal] = mapped_column() - pep604_reverse: Mapped[Decimal | float] = mapped_column() - pep604_optional: Mapped[Decimal | float | None] = ( - mapped_column() - ) - pep604_data_fwd: Mapped["float | Decimal"] = mapped_column() - pep604_reverse_fwd: Mapped["Decimal | float"] = mapped_column() - pep604_optional_fwd: Mapped["Decimal | float | None"] = ( - mapped_column() - ) + pep604_data: Mapped[float | Decimal] = mapped_column() + pep604_reverse: Mapped[Decimal | float] = mapped_column() + pep604_optional: Mapped[Decimal | float | None] = mapped_column() + pep604_data_fwd: Mapped["float | Decimal"] = mapped_column() + pep604_reverse_fwd: Mapped["Decimal | float"] = mapped_column() + pep604_optional_fwd: Mapped["Decimal | float | None"] = ( + mapped_column() + ) info = [ ("data", False), @@ -2248,16 +2211,13 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): ("refer_union", "null" in union.name), ("refer_union_optional", True), ("unflat_union_optional_data", True), + ("pep604_data", False), + ("pep604_reverse", False), + ("pep604_optional", True), + ("pep604_data_fwd", False), + ("pep604_reverse_fwd", False), + ("pep604_optional_fwd", True), ] - if compat.py310: - info += [ - ("pep604_data", False), - ("pep604_reverse", False), - ("pep604_optional", True), - ("pep604_data_fwd", False), - ("pep604_reverse_fwd", False), - ("pep604_optional_fwd", True), - ] for name, nullable in info: col = User.__table__.c[name] @@ -2274,7 +2234,7 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): "union", [ "union", - ("pep604", requires.python310), + "pep604", ("pep695", requires.python312), ], ) @@ -2321,8 +2281,8 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): "optional", "optional_fwd_ref", "union_none", - ("pep604", testing.requires.python310), - ("pep604_fwd_ref", testing.requires.python310), + "pep604", + "pep604_fwd_ref", ], ) @testing.variation("brackets", ["oneset", "twosets"]) @@ -2336,18 +2296,12 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): #12207""" class Base(DeclarativeBase): - if testing.requires.python310.enabled: - type_annotation_map = { - Dict[str, Decimal]: JSON, - dict[str, Decimal]: JSON, - Union[List[int], List[str]]: JSON, - list[int] | list[str]: JSON, - } - else: - type_annotation_map = { - Dict[str, Decimal]: JSON, - Union[List[int], List[str]]: JSON, - } + type_annotation_map = { + Dict[str, Decimal]: JSON, + dict[str, Decimal]: JSON, + Union[List[int], List[str]]: JSON, + list[int] | list[str]: JSON, + } if include_mc_type == "include_mc_type": mc = mapped_column(JSON) @@ -2365,46 +2319,36 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): if brackets.oneset: if option.not_optional: json: Mapped[Dict[str, Decimal]] = mapped_column() # type: ignore # noqa: E501 - if testing.requires.python310.enabled: - json2: Mapped[dict[str, Decimal]] = mapped_column() # type: ignore # noqa: E501 + json2: Mapped[dict[str, Decimal]] = mapped_column() # type: ignore # noqa: E501 elif option.optional: json: Mapped[Optional[Dict[str, Decimal]]] = mc - if testing.requires.python310.enabled: - json2: Mapped[Optional[dict[str, Decimal]]] = mc2 + json2: Mapped[Optional[dict[str, Decimal]]] = mc2 elif option.optional_fwd_ref: json: Mapped["Optional[Dict[str, Decimal]]"] = mc - if testing.requires.python310.enabled: - json2: Mapped["Optional[dict[str, Decimal]]"] = mc2 + json2: Mapped["Optional[dict[str, Decimal]]"] = mc2 elif option.union_none: json: Mapped[Union[Dict[str, Decimal], None]] = mc json2: Mapped[Union[None, Dict[str, Decimal]]] = mc2 elif option.pep604: json: Mapped[dict[str, Decimal] | None] = mc - if testing.requires.python310.enabled: - json2: Mapped[None | dict[str, Decimal]] = mc2 + json2: Mapped[None | dict[str, Decimal]] = mc2 elif option.pep604_fwd_ref: json: Mapped["dict[str, Decimal] | None"] = mc - if testing.requires.python310.enabled: - json2: Mapped["None | dict[str, Decimal]"] = mc2 + json2: Mapped["None | dict[str, Decimal]"] = mc2 elif brackets.twosets: if option.not_optional: json: Mapped[Union[List[int], List[str]]] = mapped_column() # type: ignore # noqa: E501 elif option.optional: json: Mapped[Optional[Union[List[int], List[str]]]] = mc - if testing.requires.python310.enabled: - json2: Mapped[ - Optional[Union[list[int], list[str]]] - ] = mc2 + json2: Mapped[Optional[Union[list[int], list[str]]]] = mc2 elif option.optional_fwd_ref: json: Mapped["Optional[Union[List[int], List[str]]]"] = mc - if testing.requires.python310.enabled: - json2: Mapped[ - "Optional[Union[list[int], list[str]]]" - ] = mc2 + json2: Mapped["Optional[Union[list[int], list[str]]]"] = ( + mc2 + ) elif option.union_none: json: Mapped[Union[List[int], List[str], None]] = mc - if testing.requires.python310.enabled: - json2: Mapped[Union[None, list[int], list[str]]] = mc2 + json2: Mapped[Union[None, list[int], list[str]]] = mc2 elif option.pep604: json: Mapped[list[int] | list[str] | None] = mc json2: Mapped[None | list[int] | list[str]] = mc2 @@ -2643,9 +2587,7 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): id: Mapped[int] = mapped_column(primary_key=True) data: Mapped[int_sub] - @testing.variation( - "dict_key", ["typing", ("plain", testing.requires.python310)] - ) + @testing.variation("dict_key", ["typing", "plain"]) def test_type_dont_mis_resolve_on_non_generic(self, dict_key): """test for #8859. @@ -3302,9 +3244,9 @@ class RelationshipLHSTest(fixtures.TestBase, testing.AssertsCompiledSQL): "datatype", [ "typing_sequence", - ("collections_sequence", testing.requires.python310), + "collections_sequence", "typing_mutable_sequence", - ("collections_mutable_sequence", testing.requires.python310), + "collections_mutable_sequence", ], ) @testing.variation("include_explicit", [True, False]) @@ -3388,12 +3330,7 @@ class RelationshipLHSTest(fixtures.TestBase, testing.AssertsCompiledSQL): @testing.variation( "collection_type", - [ - ("list", testing.requires.python310), - "List", - ("set", testing.requires.python310), - "Set", - ], + ["list", "List", "set", "Set"], ) def test_14_style_anno_accepted_w_allow_unmapped(self, collection_type): """test for #8692 and #10385""" @@ -3453,7 +3390,7 @@ class RelationshipLHSTest(fixtures.TestBase, testing.AssertsCompiledSQL): ("optional",), ("optional_fwd_ref",), ("union_none",), - ("pep604", testing.requires.python310), + ("pep604",), argnames="optional_on_m2o", ) def test_basic_bidirectional(self, decl_base, optional_on_m2o): diff --git a/test/orm/declarative/test_typed_mapping.py b/test/orm/declarative/test_typed_mapping.py index 72ba534f35..cabe5ecae2 100644 --- a/test/orm/declarative/test_typed_mapping.py +++ b/test/orm/declarative/test_typed_mapping.py @@ -6,26 +6,27 @@ import enum import inspect as _py_inspect import re import typing +from typing import Annotated from typing import Any from typing import cast from typing import ClassVar from typing import Dict from typing import Generic +from typing import get_args as get_args from typing import List +from typing import Literal as Literal from typing import NewType from typing import Optional from typing import Set from typing import Type from typing import TYPE_CHECKING +from typing import TypeAlias as TypeAlias from typing import TypedDict from typing import TypeVar from typing import Union import uuid import typing_extensions -from typing_extensions import get_args as get_args -from typing_extensions import Literal as Literal -from typing_extensions import TypeAlias as TypeAlias from typing_extensions import TypeAliasType from sqlalchemy import BIGINT @@ -93,8 +94,6 @@ from sqlalchemy.testing import requires from sqlalchemy.testing import Variation from sqlalchemy.testing.assertions import ne_ from sqlalchemy.testing.fixtures import fixture_session -from sqlalchemy.util import compat -from sqlalchemy.util.typing import Annotated TV = typing.TypeVar("TV") @@ -119,15 +118,14 @@ _JsonPrimitive: TypeAlias = Union[str, int, float, bool, None] _JsonObject: TypeAlias = Dict[str, "_Json"] _JsonArray: TypeAlias = List["_Json"] _Json: TypeAlias = Union[_JsonObject, _JsonArray, _JsonPrimitive] +_JsonPrimitivePep604: TypeAlias = str | int | float | bool | None +_JsonObjectPep604: TypeAlias = dict[str, "_JsonPep604"] +_JsonArrayPep604: TypeAlias = list["_JsonPep604"] +_JsonPep604: TypeAlias = ( + _JsonObjectPep604 | _JsonArrayPep604 | _JsonPrimitivePep604 +) +_JsonPep695 = TypeAliasType("_JsonPep695", _JsonPep604) -if compat.py310: - _JsonPrimitivePep604: TypeAlias = str | int | float | bool | None - _JsonObjectPep604: TypeAlias = dict[str, "_JsonPep604"] - _JsonArrayPep604: TypeAlias = list["_JsonPep604"] - _JsonPep604: TypeAlias = ( - _JsonObjectPep604 | _JsonArrayPep604 | _JsonPrimitivePep604 - ) - _JsonPep695 = TypeAliasType("_JsonPep695", _JsonPep604) TypingTypeAliasType = getattr(typing, "TypeAliasType", TypeAliasType) @@ -143,11 +141,10 @@ _UnionPep695 = TypeAliasType("_UnionPep695", Union[_SomeDict1, _SomeDict2]) strtypalias_keyword = TypeAliasType( "strtypalias_keyword", Annotated[str, mapped_column(info={"hi": "there"})] ) -if compat.py310: - strtypalias_keyword_nested = TypeAliasType( - "strtypalias_keyword_nested", - int | Annotated[str, mapped_column(info={"hi": "there"})], - ) +strtypalias_keyword_nested = TypeAliasType( + "strtypalias_keyword_nested", + int | Annotated[str, mapped_column(info={"hi": "there"})], +) strtypalias_ta: TypeAlias = Annotated[str, mapped_column(info={"hi": "there"})] strtypalias_plain = Annotated[str, mapped_column(info={"hi": "there"})] _Literal695 = TypeAliasType( @@ -624,11 +621,11 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): "argtype", [ "type", - ("column", testing.requires.python310), - ("mapped_column", testing.requires.python310), + "column", + "mapped_column", "column_class", "ref_to_type", - ("ref_to_column", testing.requires.python310), + "ref_to_column", ], ) def test_construct_lhs_sqlalchemy_type(self, decl_base, argtype): @@ -1299,7 +1296,6 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): eq_(col.type.enums, ["a", "b"]) is_(col.type.native_enum, False) - @testing.requires.python310 def test_we_got_all_attrs_test_annotated(self): argnames = _py_inspect.getfullargspec(mapped_column) assert _annotated_names_tested.issuperset(argnames.kwonlyargs), ( @@ -1410,7 +1406,6 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): exc.SADeprecationWarning( "Argument 'kw_only' is a dataclass argument " ), - testing.requires.python310, ), ( "compare", @@ -1418,7 +1413,6 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): exc.SADeprecationWarning( "Argument 'compare' is a dataclass argument " ), - testing.requires.python310, ), ( "default_factory", @@ -1611,17 +1605,10 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): str50 = NewType("str50", str) - if compat.py310: - text = ".*str50" - else: - # NewTypes before 3.10 had a very bad repr - # .new_type at 0x...> - text = ".*NewType.*" - with expect_raises_message( orm_exc.MappedAnnotationError, "Could not locate SQLAlchemy Core type for Python type " - f"{text} inside the 'data_one' attribute Mapped annotation", + ".*str50 inside the 'data_one' attribute Mapped annotation", ): class MyClass(decl_base): @@ -1839,38 +1826,22 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): to_assert.fail() @testing.combinations( - (collections.abc.Sequence, (str,), testing.requires.python310), - (collections.abc.MutableSequence, (str,), testing.requires.python310), - (collections.abc.Mapping, (str, str), testing.requires.python310), - ( - collections.abc.MutableMapping, - (str, str), - testing.requires.python310, - ), - (typing.Mapping, (str, str), testing.requires.python310), - (typing.MutableMapping, (str, str), testing.requires.python310), + (collections.abc.Sequence, (str,)), + (collections.abc.MutableSequence, (str,)), + (collections.abc.Mapping, (str, str)), + (collections.abc.MutableMapping, (str, str)), + (typing.Mapping, (str, str)), + (typing.MutableMapping, (str, str)), (typing.Sequence, (str,)), (typing.MutableSequence, (str,)), - (list, (str,), testing.requires.python310), - ( - List, - (str,), - ), - (dict, (str, str), testing.requires.python310), - ( - Dict, - (str, str), - ), - (list, None, testing.requires.python310), - ( - List, - None, - ), - (dict, None, testing.requires.python310), - ( - Dict, - None, - ), + (list, (str,)), + (List, (str,)), + (dict, (str, str)), + (Dict, (str, str)), + (list, None), + (List, None), + (dict, None), + (Dict, None), id_="sa", argnames="container_typ,args", ) @@ -2157,12 +2128,7 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): @testing.variation( "union", - [ - "union", - ("pep604", requires.python310), - "union_null", - ("pep604_null", requires.python310), - ], + ["union", "pep604", "union_null", "pep604_null"], ) def test_unions(self, union): # anno only: global UnionType @@ -2218,17 +2184,14 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): float_data: Mapped[float] = mapped_column() decimal_data: Mapped[Decimal] = mapped_column() - if compat.py310: - pep604_data: Mapped[float | Decimal] = mapped_column() - pep604_reverse: Mapped[Decimal | float] = mapped_column() - pep604_optional: Mapped[Decimal | float | None] = ( - mapped_column() - ) - pep604_data_fwd: Mapped["float | Decimal"] = mapped_column() - pep604_reverse_fwd: Mapped["Decimal | float"] = mapped_column() - pep604_optional_fwd: Mapped["Decimal | float | None"] = ( - mapped_column() - ) + pep604_data: Mapped[float | Decimal] = mapped_column() + pep604_reverse: Mapped[Decimal | float] = mapped_column() + pep604_optional: Mapped[Decimal | float | None] = mapped_column() + pep604_data_fwd: Mapped["float | Decimal"] = mapped_column() + pep604_reverse_fwd: Mapped["Decimal | float"] = mapped_column() + pep604_optional_fwd: Mapped["Decimal | float | None"] = ( + mapped_column() + ) info = [ ("data", False), @@ -2239,16 +2202,13 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): ("refer_union", "null" in union.name), ("refer_union_optional", True), ("unflat_union_optional_data", True), + ("pep604_data", False), + ("pep604_reverse", False), + ("pep604_optional", True), + ("pep604_data_fwd", False), + ("pep604_reverse_fwd", False), + ("pep604_optional_fwd", True), ] - if compat.py310: - info += [ - ("pep604_data", False), - ("pep604_reverse", False), - ("pep604_optional", True), - ("pep604_data_fwd", False), - ("pep604_reverse_fwd", False), - ("pep604_optional_fwd", True), - ] for name, nullable in info: col = User.__table__.c[name] @@ -2265,7 +2225,7 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): "union", [ "union", - ("pep604", requires.python310), + "pep604", ("pep695", requires.python312), ], ) @@ -2312,8 +2272,8 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): "optional", "optional_fwd_ref", "union_none", - ("pep604", testing.requires.python310), - ("pep604_fwd_ref", testing.requires.python310), + "pep604", + "pep604_fwd_ref", ], ) @testing.variation("brackets", ["oneset", "twosets"]) @@ -2327,18 +2287,12 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): #12207""" class Base(DeclarativeBase): - if testing.requires.python310.enabled: - type_annotation_map = { - Dict[str, Decimal]: JSON, - dict[str, Decimal]: JSON, - Union[List[int], List[str]]: JSON, - list[int] | list[str]: JSON, - } - else: - type_annotation_map = { - Dict[str, Decimal]: JSON, - Union[List[int], List[str]]: JSON, - } + type_annotation_map = { + Dict[str, Decimal]: JSON, + dict[str, Decimal]: JSON, + Union[List[int], List[str]]: JSON, + list[int] | list[str]: JSON, + } if include_mc_type == "include_mc_type": mc = mapped_column(JSON) @@ -2356,46 +2310,36 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): if brackets.oneset: if option.not_optional: json: Mapped[Dict[str, Decimal]] = mapped_column() # type: ignore # noqa: E501 - if testing.requires.python310.enabled: - json2: Mapped[dict[str, Decimal]] = mapped_column() # type: ignore # noqa: E501 + json2: Mapped[dict[str, Decimal]] = mapped_column() # type: ignore # noqa: E501 elif option.optional: json: Mapped[Optional[Dict[str, Decimal]]] = mc - if testing.requires.python310.enabled: - json2: Mapped[Optional[dict[str, Decimal]]] = mc2 + json2: Mapped[Optional[dict[str, Decimal]]] = mc2 elif option.optional_fwd_ref: json: Mapped["Optional[Dict[str, Decimal]]"] = mc - if testing.requires.python310.enabled: - json2: Mapped["Optional[dict[str, Decimal]]"] = mc2 + json2: Mapped["Optional[dict[str, Decimal]]"] = mc2 elif option.union_none: json: Mapped[Union[Dict[str, Decimal], None]] = mc json2: Mapped[Union[None, Dict[str, Decimal]]] = mc2 elif option.pep604: json: Mapped[dict[str, Decimal] | None] = mc - if testing.requires.python310.enabled: - json2: Mapped[None | dict[str, Decimal]] = mc2 + json2: Mapped[None | dict[str, Decimal]] = mc2 elif option.pep604_fwd_ref: json: Mapped["dict[str, Decimal] | None"] = mc - if testing.requires.python310.enabled: - json2: Mapped["None | dict[str, Decimal]"] = mc2 + json2: Mapped["None | dict[str, Decimal]"] = mc2 elif brackets.twosets: if option.not_optional: json: Mapped[Union[List[int], List[str]]] = mapped_column() # type: ignore # noqa: E501 elif option.optional: json: Mapped[Optional[Union[List[int], List[str]]]] = mc - if testing.requires.python310.enabled: - json2: Mapped[ - Optional[Union[list[int], list[str]]] - ] = mc2 + json2: Mapped[Optional[Union[list[int], list[str]]]] = mc2 elif option.optional_fwd_ref: json: Mapped["Optional[Union[List[int], List[str]]]"] = mc - if testing.requires.python310.enabled: - json2: Mapped[ - "Optional[Union[list[int], list[str]]]" - ] = mc2 + json2: Mapped["Optional[Union[list[int], list[str]]]"] = ( + mc2 + ) elif option.union_none: json: Mapped[Union[List[int], List[str], None]] = mc - if testing.requires.python310.enabled: - json2: Mapped[Union[None, list[int], list[str]]] = mc2 + json2: Mapped[Union[None, list[int], list[str]]] = mc2 elif option.pep604: json: Mapped[list[int] | list[str] | None] = mc json2: Mapped[None | list[int] | list[str]] = mc2 @@ -2634,9 +2578,7 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): id: Mapped[int] = mapped_column(primary_key=True) data: Mapped[int_sub] - @testing.variation( - "dict_key", ["typing", ("plain", testing.requires.python310)] - ) + @testing.variation("dict_key", ["typing", "plain"]) def test_type_dont_mis_resolve_on_non_generic(self, dict_key): """test for #8859. @@ -3293,9 +3235,9 @@ class RelationshipLHSTest(fixtures.TestBase, testing.AssertsCompiledSQL): "datatype", [ "typing_sequence", - ("collections_sequence", testing.requires.python310), + "collections_sequence", "typing_mutable_sequence", - ("collections_mutable_sequence", testing.requires.python310), + "collections_mutable_sequence", ], ) @testing.variation("include_explicit", [True, False]) @@ -3379,12 +3321,7 @@ class RelationshipLHSTest(fixtures.TestBase, testing.AssertsCompiledSQL): @testing.variation( "collection_type", - [ - ("list", testing.requires.python310), - "List", - ("set", testing.requires.python310), - "Set", - ], + ["list", "List", "set", "Set"], ) def test_14_style_anno_accepted_w_allow_unmapped(self, collection_type): """test for #8692 and #10385""" @@ -3444,7 +3381,7 @@ class RelationshipLHSTest(fixtures.TestBase, testing.AssertsCompiledSQL): ("optional",), ("optional_fwd_ref",), ("union_none",), - ("pep604", testing.requires.python310), + ("pep604",), argnames="optional_on_m2o", ) def test_basic_bidirectional(self, decl_base, optional_on_m2o): diff --git a/test/orm/test_cache_key.py b/test/orm/test_cache_key.py index 4bd353b84f..8f595bcaeb 100644 --- a/test/orm/test_cache_key.py +++ b/test/orm/test_cache_key.py @@ -1164,10 +1164,8 @@ class EmbeddedSubqTest( testing.skip_test("python platform not available") elif util.py311: int_within_variance(39996, total_size(ck), 0.05) - elif util.py310: - int_within_variance(29796, total_size(ck), 0.05) else: - testing.skip_test("python platform not available") + int_within_variance(29796, total_size(ck), 0.05) class WithExpresionLoaderOptTest(DeclarativeMappedTest): diff --git a/test/orm/test_deprecations.py b/test/orm/test_deprecations.py index a52a5ddacd..d70364c4d5 100644 --- a/test/orm/test_deprecations.py +++ b/test/orm/test_deprecations.py @@ -424,7 +424,7 @@ class MiscDeprecationsTest(fixtures.TestBase): @testing.combinations( ("init", True), - ("kw_only", True, testing.requires.python310), + ("kw_only", True), ("default", 5), ("default_factory", lambda: 10), argnames="paramname, value", @@ -436,7 +436,6 @@ class MiscDeprecationsTest(fixtures.TestBase): ): column_property(column("q"), **{paramname: value}) - @testing.requires.python310 def test_column_property_dc_attributes_still_function(self, dc_decl_base): with expect_deprecated( r"The column_property.init parameter is deprecated " -- 2.47.3