]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Fixes for public_factory and mysql/pg dml functions
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 8 Feb 2020 19:53:21 +0000 (14:53 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 8 Feb 2020 19:53:21 +0000 (14:53 -0500)
* ensure that the location indicated by public_factory is
  importable

* adjust all of sqlalchemy.sql.expression locations to be correct

* support the case where a public_factory is against a function
  that has another public_factory already, and already replaced the
  __init__ on the target class

* Use mysql.insert(), postgresql.insert(), don't include .dml in the
  class path.

Change-Id: Iac285289455d8d7102349df3814f7cedc758e639

doc/build/dialects/mysql.rst
doc/build/dialects/postgresql.rst
lib/sqlalchemy/dialects/mssql/base.py
lib/sqlalchemy/dialects/mysql/base.py
lib/sqlalchemy/dialects/mysql/dml.py
lib/sqlalchemy/dialects/postgresql/base.py
lib/sqlalchemy/dialects/postgresql/dml.py
lib/sqlalchemy/sql/expression.py
lib/sqlalchemy/util/langhelpers.py

index bfd345bc0482c52f973d805a0c9863c0e287dc6f..760a0a91d67fcfc9e5709b9485344b4ba9dddea5 100644 (file)
@@ -159,9 +159,9 @@ construction arguments, are as follows:
 MySQL DML Constructs
 -------------------------
 
-.. autofunction:: sqlalchemy.dialects.mysql.dml.insert
+.. autofunction:: sqlalchemy.dialects.mysql.insert
 
-.. autoclass:: sqlalchemy.dialects.mysql.dml.Insert
+.. autoclass:: sqlalchemy.dialects.mysql.Insert
   :members:
 
 
index e03fba8d30c3e112f03f1e526da2c26d6130ab4f..35ed285eb2fecc2684072a643cd00ec569449e42 100644 (file)
@@ -181,9 +181,9 @@ For example::
 PostgreSQL DML Constructs
 -------------------------
 
-.. autofunction:: sqlalchemy.dialects.postgresql.dml.insert
+.. autofunction:: sqlalchemy.dialects.postgresql.insert
 
-.. autoclass:: sqlalchemy.dialects.postgresql.dml.Insert
+.. autoclass:: sqlalchemy.dialects.postgresql.Insert
   :members:
 
 psycopg2
index 609a60f7cee1d4b9d76a196287d2c7aa14e0932d..6f3e8fb44a18c644994b6d4b5b04b218eb4018fd 100644 (file)
@@ -1220,7 +1220,7 @@ class TryCast(sql.elements.Cast):
         super(TryCast, self).__init__(*arg, **kw)
 
 
-try_cast = public_factory(TryCast, ".mssql.try_cast")
+try_cast = public_factory(TryCast, ".dialects.mssql.try_cast")
 
 # old names.
 MSDateTime = _MSDateTime
@@ -1641,10 +1641,7 @@ class MSSQLCompiler(compiler.SQLCompiler):
         """
 
         if self.dialect._supports_offset_fetch and (
-            (
-                not select._simple_int_limit
-                and select._limit_clause is not None
-            )
+            (not select._simple_int_limit and select._limit_clause is not None)
             or (
                 select._offset_clause is not None
                 and not select._simple_int_offset
@@ -1664,7 +1661,7 @@ class MSSQLCompiler(compiler.SQLCompiler):
             if select._offset_clause is not None:
                 offset_str = self.process(select._offset_clause, **kw)
             else:
-                offset_str = '0'
+                offset_str = "0"
             text += "\n OFFSET %s ROWS" % offset_str
 
             if select._limit_clause is not None:
@@ -1687,14 +1684,21 @@ class MSSQLCompiler(compiler.SQLCompiler):
         MSSQL 2012 and above are excluded
 
         """
-        if not self.dialect._supports_offset_fetch and (
-            (not select._simple_int_limit and select._limit_clause is not None)
-            or (
-                select._offset_clause is not None
-                and not select._simple_int_offset
-                or select._offset
+        if (
+            not self.dialect._supports_offset_fetch
+            and (
+                (
+                    not select._simple_int_limit
+                    and select._limit_clause is not None
+                )
+                or (
+                    select._offset_clause is not None
+                    and not select._simple_int_offset
+                    or select._offset
+                )
             )
-        ) and not getattr(select, "_mssql_visit", None):
+            and not getattr(select, "_mssql_visit", None)
+        ):
 
             # to use ROW_NUMBER(), an ORDER BY is required.
             if not select._order_by_clause.clauses:
@@ -2476,7 +2480,8 @@ class MSDialect(default.DefaultDialect):
             )
 
         self._supports_offset_fetch = (
-            self.server_version_info and self.server_version_info[0] >= 11)
+            self.server_version_info and self.server_version_info[0] >= 11
+        )
 
     def _get_default_schema_name(self, connection):
         if self.server_version_info < MS_2005_VERSION:
index 6e84c9da1d1bbe31b9cc1ac33a8f7270a8e03253..e0bf16793118d6e7efe63428ec12d16fd1195dbd 100644 (file)
@@ -352,8 +352,8 @@ will be performed.   The statement allows for separate specification of the
 values to INSERT versus the values for UPDATE.
 
 SQLAlchemy provides ``ON DUPLICATE KEY UPDATE`` support via the MySQL-specific
-:func:`.mysql.dml.insert()` function, which provides
-the generative method :meth:`~.mysql.dml.Insert.on_duplicate_key_update`::
+:func:`.mysql.insert()` function, which provides
+the generative method :meth:`~.mysql.Insert.on_duplicate_key_update`::
 
     from sqlalchemy.dialects.mysql import insert
 
@@ -377,7 +377,7 @@ an error or to skip performing an UPDATE.
 existing row, using any combination of new values as well as values
 from the proposed insertion.   These values are normally specified using
 keyword arguments passed to the
-:meth:`~.mysql.dml.Insert.on_duplicate_key_update`
+:meth:`~.mysql.Insert.on_duplicate_key_update`
 given column key values (usually the name of the column, unless it
 specifies :paramref:`.Column.key`) as keys and literal or SQL expressions
 as values::
@@ -421,8 +421,8 @@ this context is unambiguous::
 
 
 In order to refer to the proposed insertion row, the special alias
-:attr:`~.mysql.dml.Insert.inserted` is available as an attribute on
-the :class:`.mysql.dml.Insert` object; this object is a
+:attr:`~.mysql.Insert.inserted` is available as an attribute on
+the :class:`.mysql.Insert` object; this object is a
 :class:`.ColumnCollection` which contains all columns of the target
 table::
 
index b43b364fabe6a7df80e214e40520d5a72d3eb512..531b31bc33b92a7da9a1d1ddd3776fc25d66d788 100644 (file)
@@ -15,6 +15,9 @@ class Insert(StandardInsert):
 
     Adds methods for MySQL-specific syntaxes such as ON DUPLICATE KEY UPDATE.
 
+    The :class:`~.mysql.Insert` object is created using the
+    :func:`sqlalchemy.dialects.mysql.insert` function.
+
     .. versionadded:: 1.2
 
     """
@@ -105,7 +108,9 @@ class Insert(StandardInsert):
         self._post_values_clause = OnDuplicateClause(inserted_alias, values)
 
 
-insert = public_factory(Insert, ".dialects.mysql.insert")
+insert = public_factory(
+    Insert, ".dialects.mysql.insert", ".dialects.mysql.Insert"
+)
 
 
 class OnDuplicateClause(ClauseElement):
index 82c619c858e21c187f107c9de0c791d68d582d45..ceefc20b01f26254cfd0fca3fe1cd8e48f3b46a2 100644 (file)
@@ -304,9 +304,9 @@ or they may be *inferred* by stating the columns and conditions that comprise
 the indexes.
 
 SQLAlchemy provides ``ON CONFLICT`` support via the PostgreSQL-specific
-:func:`.postgresql.dml.insert()` function, which provides
-the generative methods :meth:`~.postgresql.dml.Insert.on_conflict_do_update`
-and :meth:`~.postgresql.dml.Insert.on_conflict_do_nothing`::
+:func:`.postgresql.insert()` function, which provides
+the generative methods :meth:`~.postgresql.Insert.on_conflict_do_update`
+and :meth:`~.postgresql.Insert.on_conflict_do_nothing`::
 
     from sqlalchemy.dialects.postgresql import insert
 
@@ -415,8 +415,8 @@ for UPDATE::
     :paramref:`.Insert.on_conflict_do_update.set_` dictionary.
 
 In order to refer to the proposed insertion row, the special alias
-:attr:`~.postgresql.dml.Insert.excluded` is available as an attribute on
-the :class:`.postgresql.dml.Insert` object; this object is a
+:attr:`~.postgresql.Insert.excluded` is available as an attribute on
+the :class:`.postgresql.Insert` object; this object is a
 :class:`.ColumnCollection` which alias contains all columns of the target
 table::
 
@@ -452,7 +452,7 @@ parameter, which will limit those rows which receive an UPDATE::
 ``ON CONFLICT`` may also be used to skip inserting a row entirely
 if any conflict with a unique or exclusion constraint occurs; below
 this is illustrated using the
-:meth:`~.postgresql.dml.Insert.on_conflict_do_nothing` method::
+:meth:`~.postgresql.Insert.on_conflict_do_nothing` method::
 
     from sqlalchemy.dialects.postgresql import insert
 
index ec40ae87e2e0a0b792a315421bfeabd7e85f3ef9..626f810186d57778fde3da1b7c50d25173e48e0f 100644 (file)
@@ -23,6 +23,9 @@ class Insert(StandardInsert):
 
     Adds methods for PG-specific syntaxes such as ON CONFLICT.
 
+    The :class:`.postgresql.Insert` object is created using the
+    :func:`sqlalchemy.dialects.postgresql.insert` function.
+
     .. versionadded:: 1.1
 
     """
@@ -139,7 +142,9 @@ class Insert(StandardInsert):
         )
 
 
-insert = public_factory(Insert, ".dialects.postgresql.insert")
+insert = public_factory(
+    Insert, ".dialects.postgresql.insert", ".dialects.postgresql.Insert"
+)
 
 
 class OnConflictClause(ClauseElement):
index 9cd6a179ad19caf2f4d611c0d8d903cd7feecc00..7ea0e2ad339ac1be99e942228a272626f1874073 100644 (file)
@@ -164,62 +164,66 @@ from ..util.langhelpers import public_factory  # noqa
 # the functions to be available in the sqlalchemy.sql.* namespace and
 # to be auto-cross-documenting from the function to the class itself.
 
-all_ = public_factory(CollectionAggregate._create_all, ".expression.all_")
-any_ = public_factory(CollectionAggregate._create_any, ".expression.any_")
-and_ = public_factory(BooleanClauseList.and_, ".expression.and_")
-alias = public_factory(Alias._factory, ".expression.alias")
-tablesample = public_factory(TableSample._factory, ".expression.tablesample")
-lateral = public_factory(Lateral._factory, ".expression.lateral")
-or_ = public_factory(BooleanClauseList.or_, ".expression.or_")
-bindparam = public_factory(BindParameter, ".expression.bindparam")
-select = public_factory(Select, ".expression.select")
-text = public_factory(TextClause._create_text, ".expression.text")
-table = public_factory(TableClause, ".expression.table")
-column = public_factory(ColumnClause, ".expression.column")
-over = public_factory(Over, ".expression.over")
-within_group = public_factory(WithinGroup, ".expression.within_group")
-label = public_factory(Label, ".expression.label")
-case = public_factory(Case, ".expression.case")
-cast = public_factory(Cast, ".expression.cast")
-cte = public_factory(CTE._factory, ".expression.cte")
-extract = public_factory(Extract, ".exp  # noqaression.extract")
-tuple_ = public_factory(Tuple, ".expression.tuple_")
-except_ = public_factory(CompoundSelect._create_except, ".expression.except_")
+all_ = public_factory(CollectionAggregate._create_all, ".sql.expression.all_")
+any_ = public_factory(CollectionAggregate._create_any, ".sql.expression.any_")
+and_ = public_factory(BooleanClauseList.and_, ".sql.expression.and_")
+alias = public_factory(Alias._factory, ".sql.expression.alias")
+tablesample = public_factory(
+    TableSample._factory, ".sql.expression.tablesample"
+)
+lateral = public_factory(Lateral._factory, ".sql.expression.lateral")
+or_ = public_factory(BooleanClauseList.or_, ".sql.expression.or_")
+bindparam = public_factory(BindParameter, ".sql.expression.bindparam")
+select = public_factory(Select, ".sql.expression.select")
+text = public_factory(TextClause._create_text, ".sql.expression.text")
+table = public_factory(TableClause, ".sql.expression.table")
+column = public_factory(ColumnClause, ".sql.expression.column")
+over = public_factory(Over, ".sql.expression.over")
+within_group = public_factory(WithinGroup, ".sql.expression.within_group")
+label = public_factory(Label, ".sql.expression.label")
+case = public_factory(Case, ".sql.expression.case")
+cast = public_factory(Cast, ".sql.expression.cast")
+cte = public_factory(CTE._factory, ".sql.expression.cte")
+extract = public_factory(Extract, ".sql.expression.extract")
+tuple_ = public_factory(Tuple, ".sql.expression.tuple_")
+except_ = public_factory(
+    CompoundSelect._create_except, ".sql.expression.except_"
+)
 except_all = public_factory(
-    CompoundSelect._create_except_all, ".expression.except_all"
+    CompoundSelect._create_except_all, ".sql.expression.except_all"
 )
 intersect = public_factory(
-    CompoundSelect._create_intersect, ".expression.intersect"
+    CompoundSelect._create_intersect, ".sql.expression.intersect"
 )
 intersect_all = public_factory(
-    CompoundSelect._create_intersect_all, ".expression.intersect_all"
+    CompoundSelect._create_intersect_all, ".sql.expression.intersect_all"
 )
-union = public_factory(CompoundSelect._create_union, ".expression.union")
+union = public_factory(CompoundSelect._create_union, ".sql.expression.union")
 union_all = public_factory(
-    CompoundSelect._create_union_all, ".expression.union_all"
+    CompoundSelect._create_union_all, ".sql.expression.union_all"
 )
-exists = public_factory(Exists, ".expression.exists")
+exists = public_factory(Exists, ".sql.expression.exists")
 nullsfirst = public_factory(
-    UnaryExpression._create_nullsfirst, ".expression.nullsfirst"
+    UnaryExpression._create_nullsfirst, ".sql.expression.nullsfirst"
 )
 nullslast = public_factory(
-    UnaryExpression._create_nullslast, ".expression.nullslast"
+    UnaryExpression._create_nullslast, ".sql.expression.nullslast"
 )
-asc = public_factory(UnaryExpression._create_asc, ".expression.asc")
-desc = public_factory(UnaryExpression._create_desc, ".expression.desc")
+asc = public_factory(UnaryExpression._create_asc, ".sql.expression.asc")
+desc = public_factory(UnaryExpression._create_desc, ".sql.expression.desc")
 distinct = public_factory(
-    UnaryExpression._create_distinct, ".expression.distinct"
+    UnaryExpression._create_distinct, ".sql.expression.distinct"
 )
-type_coerce = public_factory(TypeCoerce, ".expression.type_coerce")
-true = public_factory(True_._instance, ".expression.true")
-false = public_factory(False_._instance, ".expression.false")
-null = public_factory(Null._instance, ".expression.null")
-join = public_factory(Join._create_join, ".expression.join")
-outerjoin = public_factory(Join._create_outerjoin, ".expression.outerjoin")
-insert = public_factory(Insert, ".expression.insert")
-update = public_factory(Update, ".expression.update")
-delete = public_factory(Delete, ".expression.delete")
-funcfilter = public_factory(FunctionFilter, ".expression.funcfilter")
+type_coerce = public_factory(TypeCoerce, ".sql.expression.type_coerce")
+true = public_factory(True_._instance, ".sql.expression.true")
+false = public_factory(False_._instance, ".sql.expression.false")
+null = public_factory(Null._instance, ".sql.expression.null")
+join = public_factory(Join._create_join, ".sql.expression.join")
+outerjoin = public_factory(Join._create_outerjoin, ".sql.expression.outerjoin")
+insert = public_factory(Insert, ".sql.expression.insert")
+update = public_factory(Update, ".sql.expression.update")
+delete = public_factory(Delete, ".sql.expression.delete")
+funcfilter = public_factory(FunctionFilter, ".sql.expression.funcfilter")
 
 
 # internal functions still being called from tests and the ORM,
index 402f8bb0932efed5f1563eb926c34fe1ee1f9695..41a9698c7d16cebf31e8a2e21b0479ec62c5d842 100644 (file)
@@ -170,7 +170,7 @@ def _exec_code_in_env(code, env, fn_name):
     return env[fn_name]
 
 
-def public_factory(target, location):
+def public_factory(target, location, class_location=None):
     """Produce a wrapping function for the given cls or classmethod.
 
     Rationale here is so that the __init__ method of the
@@ -183,14 +183,14 @@ def public_factory(target, location):
         doc = (
             "Construct a new :class:`.%s` object. \n\n"
             "This constructor is mirrored as a public API function; "
-            "see :func:`~%s` "
+            "see :func:`sqlalchemy%s` "
             "for a full usage and argument description."
             % (target.__name__, location)
         )
     else:
         fn = callable_ = target
         doc = (
-            "This function is mirrored; see :func:`~%s` "
+            "This function is mirrored; see :func:`sqlalchemy%s` "
             "for a description of arguments." % location
         )
 
@@ -209,12 +209,38 @@ def %(name)s(%(args)s):
     env = {"cls": callable_, "symbol": symbol}
     exec(code, env)
     decorated = env[location_name]
-    decorated.__doc__ = fn.__doc__
+    if hasattr(fn, "_linked_to"):
+        linked_to, linked_to_location = fn._linked_to
+        linked_to_doc = linked_to.__doc__
+        if class_location is None:
+            class_location = "%s.%s" % (target.__module__, target.__name__)
+
+        linked_to_doc = inject_docstring_text(
+            linked_to_doc,
+            ".. container:: inherited_member\n\n    "
+            "Inherited from :func:`sqlalchemy%s`; this constructor "
+            "creates a :class:`%s` object"
+            % (linked_to_location, class_location),
+            0,
+        )
+        decorated.__doc__ = linked_to_doc
+    else:
+        decorated.__doc__ = fn.__doc__
+
     decorated.__module__ = "sqlalchemy" + location.rsplit(".", 1)[0]
+    if decorated.__module__ not in sys.modules:
+        raise ImportError(
+            "public_factory location %s is not in sys.modules"
+            % (decorated.__module__,)
+        )
     if compat.py2k or hasattr(fn, "__func__"):
         fn.__func__.__doc__ = doc
+        if not hasattr(fn.__func__, "_linked_to"):
+            fn.__func__._linked_to = (decorated, location)
     else:
         fn.__doc__ = doc
+        if not hasattr(fn, "_linked_to"):
+            fn._linked_to = (decorated, location)
     return decorated