diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-04-13 09:45:29 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-04-15 10:29:23 -0400 |
| commit | c932123bacad9bf047d160b85e3f95d396c513ae (patch) | |
| tree | 3f84221c467ff8fba468d7ca78dc4b0c158d8970 /lib/sqlalchemy/sql/ddl.py | |
| parent | 0bfb620009f668e97ad3c2c25a564ca36428b9ae (diff) | |
| download | sqlalchemy-c932123bacad9bf047d160b85e3f95d396c513ae.tar.gz | |
pep484: schema API
implement strict typing for schema.py
this module has lots of public API, lots of old decisions
and very hard to follow construction sequences in many
cases, and is also where we get a lot of new feature requests,
so strict typing should help keep things clean.
among improvements here, fixed the pool .info getters
and also figured out how to get ColumnCollection and
related to be covariant so that we may set them up
as returning Column or ColumnClause without any conflicts.
DDL was affected, noting that superclasses of DDLElement
(_DDLCompiles, added recently) can now be passed into
"ddl_if" callables; reorganized ddl into ExecutableDDLElement
as a new name for DDLElement and _DDLCompiles renamed to
BaseDDLElement.
setting up strict also located an API use case that
is completely broken, which is connection.execute(some_default)
returns a scalar value. This case has been deprecated
and new paths have been set up so that connection.scalar()
may be used. This likely wasn't possible in previous
versions because scalar() would assume a CursorResult.
The scalar() change also impacts Session as we have explicit
support (since someone had reported it as a regression)
for session.execute(Sequence()) to work. They will get the
same deprecation message (which omits the word "Connection",
just uses ".execute()" and ".scalar()") and they can then
use Session.scalar() as well. Getting this to type
correctly while still supporting ORM use cases required
some refactoring, and I also set up a keyword only delimeter
for Session.execute() and related as execution_options /
bind_arguments should always be keyword only, applied these
changes to AsyncSession as well.
Additionally simpify Table __init__ now that we are Python
3 only, we can have positional plus explicit kwargs finally.
Simplify Column.__init__ as well again taking advantage
of kw only arguments.
Fill in most/all __init__ methods in sqltypes.py as
the constructor for types is most of the API. should
likely do this for dialect-specific types as well.
Apply _InfoType for all info attributes as should have been
done originally and update descriptor decorators.
Change-Id: I3f9f8ff3f1c8858471ff4545ac83d68c88107527
Diffstat (limited to 'lib/sqlalchemy/sql/ddl.py')
| -rw-r--r-- | lib/sqlalchemy/sql/ddl.py | 109 |
1 files changed, 69 insertions, 40 deletions
diff --git a/lib/sqlalchemy/sql/ddl.py b/lib/sqlalchemy/sql/ddl.py index 131ae9ef1..6ac7c2448 100644 --- a/lib/sqlalchemy/sql/ddl.py +++ b/lib/sqlalchemy/sql/ddl.py @@ -33,6 +33,7 @@ if typing.TYPE_CHECKING: from .compiler import Compiled from .compiler import DDLCompiler from .elements import BindParameter + from .schema import Constraint from .schema import ForeignKeyConstraint from .schema import SchemaItem from .schema import Table @@ -43,7 +44,14 @@ if typing.TYPE_CHECKING: from ..engine.interfaces import Dialect -class _DDLCompiles(ClauseElement): +class BaseDDLElement(ClauseElement): + """The root of DDL constructs, including those that are sub-elements + within the "create table" and other processes. + + .. versionadded:: 2.0 + + """ + _hierarchy_supports_caching = False """disable cache warnings for all _DDLCompiles subclasses. """ @@ -71,10 +79,10 @@ class _DDLCompiles(ClauseElement): class DDLIfCallable(Protocol): def __call__( self, - ddl: "DDLElement", - target: "SchemaItem", - bind: Optional["Connection"], - tables: Optional[List["Table"]] = None, + ddl: BaseDDLElement, + target: SchemaItem, + bind: Optional[Connection], + tables: Optional[List[Table]] = None, state: Optional[Any] = None, *, dialect: Dialect, @@ -89,7 +97,14 @@ class DDLIf(typing.NamedTuple): callable_: Optional[DDLIfCallable] state: Optional[Any] - def _should_execute(self, ddl, target, bind, compiler=None, **kw): + def _should_execute( + self, + ddl: BaseDDLElement, + target: SchemaItem, + bind: Optional[Connection], + compiler: Optional[DDLCompiler] = None, + **kw: Any, + ) -> bool: if bind is not None: dialect = bind.dialect elif compiler is not None: @@ -117,18 +132,23 @@ class DDLIf(typing.NamedTuple): return True -SelfDDLElement = typing.TypeVar("SelfDDLElement", bound="DDLElement") +SelfExecutableDDLElement = typing.TypeVar( + "SelfExecutableDDLElement", bound="ExecutableDDLElement" +) -class DDLElement(roles.DDLRole, Executable, _DDLCompiles): - """Base class for DDL expression constructs. +class ExecutableDDLElement(roles.DDLRole, Executable, BaseDDLElement): + """Base class for standalone executable DDL expression constructs. This class is the base for the general purpose :class:`.DDL` class, as well as the various create/drop clause constructs such as :class:`.CreateTable`, :class:`.DropTable`, :class:`.AddConstraint`, etc. - :class:`.DDLElement` integrates closely with SQLAlchemy events, + .. versionchanged:: 2.0 :class:`.ExecutableDDLElement` is renamed from + :class:`.DDLElement`, which still exists for backwards compatibility. + + :class:`.ExecutableDDLElement` integrates closely with SQLAlchemy events, introduced in :ref:`event_toplevel`. An instance of one is itself an event receiving callable:: @@ -161,29 +181,31 @@ class DDLElement(roles.DDLRole, Executable, _DDLCompiles): ) @_generative - def against(self: SelfDDLElement, target: SchemaItem) -> SelfDDLElement: - """Return a copy of this :class:`_schema.DDLElement` which will include - the given target. - - This essentially applies the given item to the ``.target`` attribute - of the returned :class:`_schema.DDLElement` object. This target + def against( + self: SelfExecutableDDLElement, target: SchemaItem + ) -> SelfExecutableDDLElement: + """Return a copy of this :class:`_schema.ExecutableDDLElement` which + will include the given target. + + This essentially applies the given item to the ``.target`` attribute of + the returned :class:`_schema.ExecutableDDLElement` object. This target is then usable by event handlers and compilation routines in order to provide services such as tokenization of a DDL string in terms of a particular :class:`_schema.Table`. - When a :class:`_schema.DDLElement` object is established as an event - handler for the :meth:`_events.DDLEvents.before_create` or - :meth:`_events.DDLEvents.after_create` events, and the event - then occurs for a given target such as a :class:`_schema.Constraint` - or :class:`_schema.Table`, that target is established with a copy - of the :class:`_schema.DDLElement` object using this method, which - then proceeds to the :meth:`_schema.DDLElement.execute` method - in order to invoke the actual DDL instruction. + When a :class:`_schema.ExecutableDDLElement` object is established as + an event handler for the :meth:`_events.DDLEvents.before_create` or + :meth:`_events.DDLEvents.after_create` events, and the event then + occurs for a given target such as a :class:`_schema.Constraint` or + :class:`_schema.Table`, that target is established with a copy of the + :class:`_schema.ExecutableDDLElement` object using this method, which + then proceeds to the :meth:`_schema.ExecutableDDLElement.execute` + method in order to invoke the actual DDL instruction. :param target: a :class:`_schema.SchemaItem` that will be the subject of a DDL operation. - :return: a copy of this :class:`_schema.DDLElement` with the + :return: a copy of this :class:`_schema.ExecutableDDLElement` with the ``.target`` attribute assigned to the given :class:`_schema.SchemaItem`. @@ -198,13 +220,14 @@ class DDLElement(roles.DDLRole, Executable, _DDLCompiles): @_generative def execute_if( - self: SelfDDLElement, + self: SelfExecutableDDLElement, dialect: Optional[str] = None, callable_: Optional[DDLIfCallable] = None, state: Optional[Any] = None, - ) -> SelfDDLElement: + ) -> SelfExecutableDDLElement: r"""Return a callable that will execute this - :class:`_ddl.DDLElement` conditionally within an event handler. + :class:`_ddl.ExecutableDDLElement` conditionally within an event + handler. Used to provide a wrapper for event listening:: @@ -302,7 +325,11 @@ class DDLElement(roles.DDLRole, Executable, _DDLCompiles): return s -class DDL(DDLElement): +DDLElement = ExecutableDDLElement +""":class:`.DDLElement` is renamed to :class:`.ExecutableDDLElement`.""" + + +class DDL(ExecutableDDLElement): """A literal DDL statement. Specifies literal SQL DDL to be executed by the database. DDL objects @@ -390,7 +417,7 @@ class DDL(DDLElement): ) -class _CreateDropBase(DDLElement): +class _CreateDropBase(ExecutableDDLElement): """Base class for DDL constructs that represent CREATE and DROP or equivalents. @@ -484,9 +511,11 @@ class CreateTable(_CreateDropBase): def __init__( self, - element, - include_foreign_key_constraints=None, - if_not_exists=False, + element: Table, + include_foreign_key_constraints: Optional[ + typing_Sequence[ForeignKeyConstraint] + ] = None, + if_not_exists: bool = False, ): """Create a :class:`.CreateTable` construct. @@ -522,12 +551,12 @@ class _DropView(_CreateDropBase): __visit_name__ = "drop_view" -class CreateConstraint(_DDLCompiles): - def __init__(self, element): +class CreateConstraint(BaseDDLElement): + def __init__(self, element: Constraint): self.element = element -class CreateColumn(_DDLCompiles): +class CreateColumn(BaseDDLElement): """Represent a :class:`_schema.Column` as rendered in a CREATE TABLE statement, via the :class:`.CreateTable` construct. @@ -641,7 +670,7 @@ class DropTable(_CreateDropBase): __visit_name__ = "drop_table" - def __init__(self, element, if_exists=False): + def __init__(self, element: Table, if_exists: bool = False): """Create a :class:`.DropTable` construct. :param element: a :class:`_schema.Table` that's the subject @@ -761,12 +790,12 @@ class DropColumnComment(_CreateDropBase): __visit_name__ = "drop_column_comment" -class DDLBase(SchemaVisitor): +class InvokeDDLBase(SchemaVisitor): def __init__(self, connection): self.connection = connection -class SchemaGenerator(DDLBase): +class SchemaGenerator(InvokeDDLBase): def __init__( self, dialect, connection, checkfirst=False, tables=None, **kwargs ): @@ -925,7 +954,7 @@ class SchemaGenerator(DDLBase): CreateIndex(index)._invoke_with(self.connection) -class SchemaDropper(DDLBase): +class SchemaDropper(InvokeDDLBase): def __init__( self, dialect, connection, checkfirst=False, tables=None, **kwargs ): |
