From 43f6ae639ca0186f4802255861acdc20f19e702f Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 2 Jan 2022 17:35:43 -0500 Subject: initial reorganize for static typing start applying foundational annotations to key elements. two main elements addressed here: 1. removal of public_factory() and replacement with explicit functions. this just works much better with typing. 2. typing support for column expressions and operators. The biggest part of this involves stubbing out all the ColumnOperators methods under ColumnElement in a TYPE_CHECKING section. Took me a while to see this method vs. much more complicated things I thought I needed. Also for this version implementing #7519, ColumnElement types against the Python type and not TypeEngine. it is hoped this leads to easier transferrence between ORM/Core as well as eventual support for result set typing. Not clear yet how well this approach will work and what new issues it may introduce. given the current approach we now get full, rich typing for scenarios like this: from sqlalchemy import column, Integer, String, Boolean c1 = column('a', String) c2 = column('a', Integer) expr1 = c2.in_([1, 2, 3]) expr2 = c2 / 5 expr3 = -c2 expr4_a = ~(c2 == 5) expr4_b = ~column('q', Boolean) expr5 = c1 + 'x' expr6 = c2 + 10 Fixes: #7519 Fixes: #6810 Change-Id: I078d9f57955549f6f7868314287175f6c61c44cb --- lib/sqlalchemy/dialects/mssql/base.py | 46 ++++++++++++++--------------- lib/sqlalchemy/dialects/mysql/dml.py | 25 ++++++++++++---- lib/sqlalchemy/dialects/postgresql/array.py | 3 +- lib/sqlalchemy/dialects/postgresql/dml.py | 27 +++++++++++++---- lib/sqlalchemy/dialects/sqlite/dml.py | 27 +++++++++++++---- 5 files changed, 84 insertions(+), 44 deletions(-) (limited to 'lib/sqlalchemy/dialects') diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index b57e89a41..d48cca2a8 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -848,7 +848,6 @@ from ...types import SMALLINT from ...types import TEXT from ...types import VARCHAR from ...util import update_wrapper -from ...util.langhelpers import public_factory # https://sqlserverbuilds.blogspot.com/ @@ -1347,41 +1346,40 @@ class SQL_VARIANT(sqltypes.TypeEngine): __visit_name__ = "SQL_VARIANT" -class TryCast(sql.elements.Cast): - """Represent a SQL Server TRY_CAST expression.""" +def try_cast(*arg, **kw): + """Create a TRY_CAST expression. - __visit_name__ = "try_cast" + :class:`.TryCast` is a subclass of SQLAlchemy's :class:`.Cast` + construct, and works in the same way, except that the SQL expression + rendered is "TRY_CAST" rather than "CAST":: - stringify_dialect = "mssql" - inherit_cache = True + from sqlalchemy import select + from sqlalchemy import Numeric + from sqlalchemy.dialects.mssql import try_cast - def __init__(self, *arg, **kw): - """Create a TRY_CAST expression. + stmt = select( + try_cast(product_table.c.unit_price, Numeric(10, 4)) + ) - :class:`.TryCast` is a subclass of SQLAlchemy's :class:`.Cast` - construct, and works in the same way, except that the SQL expression - rendered is "TRY_CAST" rather than "CAST":: + The above would render:: - from sqlalchemy import select - from sqlalchemy import Numeric - from sqlalchemy.dialects.mssql import try_cast + SELECT TRY_CAST (product_table.unit_price AS NUMERIC(10, 4)) + FROM product_table - stmt = select( - try_cast(product_table.c.unit_price, Numeric(10, 4)) - ) + .. versionadded:: 1.3.7 - The above would render:: + """ + return TryCast(*arg, **kw) - SELECT TRY_CAST (product_table.unit_price AS NUMERIC(10, 4)) - FROM product_table - .. versionadded:: 1.3.7 +class TryCast(sql.elements.Cast): + """Represent a SQL Server TRY_CAST expression.""" - """ - super(TryCast, self).__init__(*arg, **kw) + __visit_name__ = "try_cast" + stringify_dialect = "mssql" + inherit_cache = True -try_cast = public_factory(TryCast, ".dialects.mssql.try_cast") # old names. MSDateTime = _MSDateTime diff --git a/lib/sqlalchemy/dialects/mysql/dml.py b/lib/sqlalchemy/dialects/mysql/dml.py index a21d49e0b..eb4a9f798 100644 --- a/lib/sqlalchemy/dialects/mysql/dml.py +++ b/lib/sqlalchemy/dialects/mysql/dml.py @@ -14,12 +14,30 @@ from ...sql.base import ColumnCollection from ...sql.dml import Insert as StandardInsert from ...sql.elements import ClauseElement from ...sql.expression import alias -from ...util.langhelpers import public_factory __all__ = ("Insert", "insert") +def insert(table): + """Construct a MySQL/MariaDB-specific variant :class:`_mysql.Insert` + construct. + + .. container:: inherited_member + + The :func:`sqlalchemy.dialects.mysql.insert` function creates + a :class:`sqlalchemy.dialects.mysql.Insert`. This class is based + on the dialect-agnostic :class:`_sql.Insert` construct which may + be constructed using the :func:`_sql.insert` function in + SQLAlchemy Core. + + The :class:`_mysql.Insert` construct includes additional methods + :meth:`_mysql.Insert.on_duplicate_key_update`. + + """ + return Insert(table) + + SelfInsert = typing.TypeVar("SelfInsert", bound="Insert") @@ -145,11 +163,6 @@ class Insert(StandardInsert): return self -insert = public_factory( - Insert, ".dialects.mysql.insert", ".dialects.mysql.Insert" -) - - class OnDuplicateClause(ClauseElement): __visit_name__ = "on_duplicate_key_update" diff --git a/lib/sqlalchemy/dialects/postgresql/array.py b/lib/sqlalchemy/dialects/postgresql/array.py index f30a409c7..abe17ea35 100644 --- a/lib/sqlalchemy/dialects/postgresql/array.py +++ b/lib/sqlalchemy/dialects/postgresql/array.py @@ -94,13 +94,12 @@ class array(expression.ClauseList, expression.ColumnElement): coercions.expect(roles.ExpressionElementRole, c) for c in clauses ] - super(array, self).__init__(*clauses, **kw) - self._type_tuple = [arg.type for arg in clauses] main_type = kw.pop( "type_", self._type_tuple[0] if self._type_tuple else sqltypes.NULLTYPE, ) + super(array, self).__init__(*clauses, **kw) if isinstance(main_type, ARRAY): self.type = ARRAY( diff --git a/lib/sqlalchemy/dialects/postgresql/dml.py b/lib/sqlalchemy/dialects/postgresql/dml.py index 48b180e81..09dbd9558 100644 --- a/lib/sqlalchemy/dialects/postgresql/dml.py +++ b/lib/sqlalchemy/dialects/postgresql/dml.py @@ -17,11 +17,31 @@ from ...sql.base import ColumnCollection from ...sql.dml import Insert as StandardInsert from ...sql.elements import ClauseElement from ...sql.expression import alias -from ...util.langhelpers import public_factory __all__ = ("Insert", "insert") + +def insert(table): + """Construct a PostgreSQL-specific variant :class:`_postgresql.Insert` + construct. + + .. container:: inherited_member + + The :func:`sqlalchemy.dialects.postgresql.insert` function creates + a :class:`sqlalchemy.dialects.postgresql.Insert`. This class is based + on the dialect-agnostic :class:`_sql.Insert` construct which may + be constructed using the :func:`_sql.insert` function in + SQLAlchemy Core. + + The :class:`_postgresql.Insert` construct includes additional methods + :meth:`_postgresql.Insert.on_conflict_do_update`, + :meth:`_postgresql.Insert.on_conflict_do_nothing`. + + """ + return Insert(table) + + SelfInsert = typing.TypeVar("SelfInsert", bound="Insert") @@ -183,11 +203,6 @@ class Insert(StandardInsert): return self -insert = public_factory( - Insert, ".dialects.postgresql.insert", ".dialects.postgresql.Insert" -) - - class OnConflictClause(ClauseElement): stringify_dialect = "postgresql" diff --git a/lib/sqlalchemy/dialects/sqlite/dml.py b/lib/sqlalchemy/dialects/sqlite/dml.py index 9284070df..7dee7e3b6 100644 --- a/lib/sqlalchemy/dialects/sqlite/dml.py +++ b/lib/sqlalchemy/dialects/sqlite/dml.py @@ -15,11 +15,31 @@ from ...sql.base import ColumnCollection from ...sql.dml import Insert as StandardInsert from ...sql.elements import ClauseElement from ...sql.expression import alias -from ...util.langhelpers import public_factory __all__ = ("Insert", "insert") + +def insert(table): + """Construct a sqlite-specific variant :class:`_sqlite.Insert` + construct. + + .. container:: inherited_member + + The :func:`sqlalchemy.dialects.sqlite.insert` function creates + a :class:`sqlalchemy.dialects.sqlite.Insert`. This class is based + on the dialect-agnostic :class:`_sql.Insert` construct which may + be constructed using the :func:`_sql.insert` function in + SQLAlchemy Core. + + The :class:`_sqlite.Insert` construct includes additional methods + :meth:`_sqlite.Insert.on_conflict_do_update`, + :meth:`_sqlite.Insert.on_conflict_do_nothing`. + + """ + return Insert(table) + + SelfInsert = typing.TypeVar("SelfInsert", bound="Insert") @@ -151,11 +171,6 @@ class Insert(StandardInsert): return self -insert = public_factory( - Insert, ".dialects.sqlite.insert", ".dialects.sqlite.Insert" -) - - class OnConflictClause(ClauseElement): stringify_dialect = "sqlite" -- cgit v1.2.1