]> 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 20:00:00 +0000 (15:00 -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
(cherry picked from commit 660ff51df0433607b12c58a12c7355107c1773f5)

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 01f84d16e45c1c75571dbd7c0c7681ffb89f4043..9642496be6c52d4cd117f438b2fec7647361d832 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 a1294cc4189d25aebc27cae091432bd86a55c35f..5fc451515ad42b39d90c905a3f62139cbd88de43 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 10ed0cf891852e99b51b53259b63ec038c76339c..839079168a8a662960e69c4df2a3e3012fe7bb44 100644 (file)
@@ -1206,7 +1206,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
index c6a4404b78301c5ad56117b5312271cb61bea81a..40b14ad4ab84f088847ddafbde95cff7b93676bb 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 293aa426da122713c606d645b3b0e4fbf6cac0c0..7a95f815c51d7c1868c86dee8679c9601d6afc39 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
 
     """
@@ -106,7 +109,9 @@ class Insert(StandardInsert):
         return self
 
 
-insert = public_factory(Insert, ".dialects.mysql.insert")
+insert = public_factory(
+    Insert, ".dialects.mysql.insert", ".dialects.mysql.Insert"
+)
 
 
 class OnDuplicateClause(ClauseElement):
index 8d33219c3fc839a65fd44c51a72454c9e59b1777..0bd2403ef76cea6359682533ec22c341875f11b2 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 9251e713060c48c1677f972ea56d419853f5d078..21ef45d525a3411ac1e0b0af4036ddc9432c65ad 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
 
     """
@@ -141,7 +144,9 @@ class Insert(StandardInsert):
         return self
 
 
-insert = public_factory(Insert, ".dialects.postgresql.insert")
+insert = public_factory(
+    Insert, ".dialects.postgresql.insert", ".dialects.postgresql.Insert"
+)
 
 
 class OnConflictClause(ClauseElement):
index 2c55e860a8f9cc69d283d321eabe06db204ffef3..e02e4ee1e07b23846e84a77706cc7c781128cbf7 100644 (file)
@@ -177,62 +177,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 80ec0861fb399a9c7ee95b65a7f2ffef0c5526b5..360c91371c0e55737c27faebea47be42e964fb7c 100644 (file)
@@ -160,7 +160,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
@@ -173,14 +173,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
         )
 
@@ -199,12 +199,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