summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2010-08-14 20:52:57 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2010-08-14 20:52:57 -0400
commitdc31eb352aed6b578b7cf0bbc0d4290b6d11f2b2 (patch)
treef57fb2bf5a70d46aa0515ad488195d5d1f3e93c0 /lib/sqlalchemy
parent504fd37d6848005730c8c68769ba9e06f83551dc (diff)
parentff1d504fc4b7df2284b9f261cd03a7568b7038e8 (diff)
downloadsqlalchemy-dc31eb352aed6b578b7cf0bbc0d4290b6d11f2b2.tar.gz
merge tip
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/dialects/postgresql/base.py9
-rw-r--r--lib/sqlalchemy/dialects/postgresql/psycopg2.py71
-rw-r--r--lib/sqlalchemy/engine/base.py4
-rwxr-xr-xlib/sqlalchemy/ext/declarative.py2
-rw-r--r--lib/sqlalchemy/orm/__init__.py27
-rw-r--r--lib/sqlalchemy/orm/mapper.py7
-rw-r--r--lib/sqlalchemy/orm/properties.py190
-rw-r--r--lib/sqlalchemy/orm/query.py4
-rw-r--r--lib/sqlalchemy/orm/scoping.py6
-rw-r--r--lib/sqlalchemy/orm/session.py56
-rw-r--r--lib/sqlalchemy/schema.py11
-rw-r--r--lib/sqlalchemy/sql/expression.py986
-rw-r--r--lib/sqlalchemy/test/testing.py1
-rw-r--r--lib/sqlalchemy/types.py5
-rw-r--r--lib/sqlalchemy/util.py41
15 files changed, 859 insertions, 561 deletions
diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py
index 5eba16372..89769b8c0 100644
--- a/lib/sqlalchemy/dialects/postgresql/base.py
+++ b/lib/sqlalchemy/dialects/postgresql/base.py
@@ -39,16 +39,17 @@ apply; no RETURNING clause is emitted nor is the sequence pre-executed in this
case.
To force the usage of RETURNING by default off, specify the flag
-``implicit_returning=False`` to :func:`create_engine`.
+``implicit_returning=False`` to :func:`.create_engine`.
Transaction Isolation Level
---------------------------
-:func:`create_engine` accepts an ``isolation_level`` parameter which results
+:func:`.create_engine` accepts an ``isolation_level`` parameter which results
in the command ``SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL
<level>`` being invoked for every new connection. Valid values for this
parameter are ``READ_COMMITTED``, ``READ_UNCOMMITTED``, ``REPEATABLE_READ``,
-and ``SERIALIZABLE``.
+and ``SERIALIZABLE``. Note that the psycopg2 dialect does *not* use this
+technique and uses psycopg2-specific APIs (see that dialect for details).
INSERT/UPDATE...RETURNING
-------------------------
@@ -57,7 +58,7 @@ The dialect supports PG 8.2's ``INSERT..RETURNING``, ``UPDATE..RETURNING`` and
``DELETE..RETURNING`` syntaxes. ``INSERT..RETURNING`` is used by default
for single-row INSERT statements in order to fetch newly generated
primary key identifiers. To specify an explicit ``RETURNING`` clause,
-use the :meth:`_UpdateBase.returning` method on a per-statement basis::
+use the :meth:`._UpdateBase.returning` method on a per-statement basis::
# INSERT..RETURNING
result = table.insert().returning(table.c.col1, table.c.col2).\\
diff --git a/lib/sqlalchemy/dialects/postgresql/psycopg2.py b/lib/sqlalchemy/dialects/postgresql/psycopg2.py
index aa5f8d32f..04b4e1fb7 100644
--- a/lib/sqlalchemy/dialects/postgresql/psycopg2.py
+++ b/lib/sqlalchemy/dialects/postgresql/psycopg2.py
@@ -12,30 +12,57 @@ Note that psycopg1 is **not** supported.
Unicode
-------
-By default, the Psycopg2 driver uses the ``psycopg2.extensions.UNICODE`` extension, such that the DBAPI receives and returns all strings as Python Unicode objects directly - SQLAlchemy passes these values through without change. Note that this setting requires that the PG client encoding be set to one which can accomodate the kind of character data being passed - typically ``utf-8``. If the Postgresql database is configured for ``SQL_ASCII`` encoding, which is often the default for PG installations, it may be necessary for non-ascii strings to be encoded into a specific encoding before being passed to the DBAPI. If changing the database's client encoding setting is not an option, specify ``use_native_unicode=False`` as a keyword argument to ``create_engine()``, and take note of the ``encoding`` setting as well, which also defaults to ``utf-8``. Note that disabling "native unicode" mode has a slight performance penalty, as SQLAlchemy now must translate unicode strings to/from an encoding such as utf-8, a task that is handled more efficiently within the Psycopg2 driver natively.
+By default, the Psycopg2 driver uses the ``psycopg2.extensions.UNICODE``
+extension, such that the DBAPI receives and returns all strings as Python
+Unicode objects directly - SQLAlchemy passes these values through without
+change. Note that this setting requires that the PG client encoding be set to
+one which can accomodate the kind of character data being passed - typically
+``utf-8``. If the Postgresql database is configured for ``SQL_ASCII``
+encoding, which is often the default for PG installations, it may be necessary
+for non-ascii strings to be encoded into a specific encoding before being
+passed to the DBAPI. If changing the database's client encoding setting is not
+an option, specify ``use_native_unicode=False`` as a keyword argument to
+``create_engine()``, and take note of the ``encoding`` setting as well, which
+also defaults to ``utf-8``. Note that disabling "native unicode" mode has a
+slight performance penalty, as SQLAlchemy now must translate unicode strings
+to/from an encoding such as utf-8, a task that is handled more efficiently
+within the Psycopg2 driver natively.
Connecting
----------
-URLs are of the form ``postgresql+psycopg2://user:password@host:port/dbname[?key=value&key=value...]``.
+URLs are of the form
+``postgresql+psycopg2://user:password@host:port/dbname[?key=value&key=value...]``.
-psycopg2-specific keyword arguments which are accepted by :func:`~sqlalchemy.create_engine()` are:
+psycopg2-specific keyword arguments which are accepted by
+:func:`.create_engine()` are:
-* *server_side_cursors* - Enable the usage of "server side cursors" for SQL statements which support
- this feature. What this essentially means from a psycopg2 point of view is that the cursor is
- created using a name, e.g. `connection.cursor('some name')`, which has the effect that result rows
- are not immediately pre-fetched and buffered after statement execution, but are instead left
- on the server and only retrieved as needed. SQLAlchemy's :class:`~sqlalchemy.engine.base.ResultProxy`
- uses special row-buffering behavior when this feature is enabled, such that groups of 100 rows
- at a time are fetched over the wire to reduce conversational overhead.
-* *use_native_unicode* - Enable the usage of Psycopg2 "native unicode" mode per connection. True
- by default.
+* *server_side_cursors* - Enable the usage of "server side cursors" for SQL
+ statements which support this feature. What this essentially means from a
+ psycopg2 point of view is that the cursor is created using a name, e.g.
+ `connection.cursor('some name')`, which has the effect that result rows are
+ not immediately pre-fetched and buffered after statement execution, but are
+ instead left on the server and only retrieved as needed. SQLAlchemy's
+ :class:`~sqlalchemy.engine.base.ResultProxy` uses special row-buffering
+ behavior when this feature is enabled, such that groups of 100 rows at a
+ time are fetched over the wire to reduce conversational overhead.
+* *use_native_unicode* - Enable the usage of Psycopg2 "native unicode" mode
+ per connection. True by default.
Transactions
------------
The psycopg2 dialect fully supports SAVEPOINT and two-phase commit operations.
+Transaction Isolation Level
+---------------------------
+
+The ``isolation_level`` parameter of :func:`.create_engine` here makes use
+psycopg2's ``set_isolation_level()`` connection method, rather than
+issuing a ``SET SESSION CHARACTERISTICS`` command. This because psycopg2
+resets the isolation level on each new transaction, and needs to know
+at the API level what level should be used.
+
NOTICE logging
---------------
@@ -208,7 +235,25 @@ class PGDialect_psycopg2(PGDialect):
return psycopg
def on_connect(self):
- base_on_connect = super(PGDialect_psycopg2, self).on_connect()
+ if self.isolation_level is not None:
+ extensions = __import__('psycopg2.extensions').extensions
+ isol = {
+ 'READ_COMMITTED':extensions.ISOLATION_LEVEL_READ_COMMITTED,
+ 'READ_UNCOMMITTED':extensions.ISOLATION_LEVEL_READ_UNCOMMITTED,
+ 'REPEATABLE_READ':extensions.ISOLATION_LEVEL_REPEATABLE_READ,
+ 'SERIALIZABLE':extensions.ISOLATION_LEVEL_SERIALIZABLE
+
+ }
+ def base_on_connect(conn):
+ try:
+ conn.set_isolation_level(isol[self.isolation_level])
+ except:
+ raise exc.InvalidRequestError(
+ "Invalid isolation level: '%s'" %
+ self.isolation_level)
+ else:
+ base_on_connect = None
+
if self.dbapi and self.use_native_unicode:
extensions = __import__('psycopg2.extensions').extensions
def connect(conn):
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py
index 8d1138316..dbba2b62f 100644
--- a/lib/sqlalchemy/engine/base.py
+++ b/lib/sqlalchemy/engine/base.py
@@ -2388,9 +2388,9 @@ class ResultProxy(object):
return self.context._inserted_primary_key
- @util.deprecated("Use inserted_primary_key")
+ @util.deprecated("0.6", "Use :attr:`.ResultProxy.inserted_primary_key`")
def last_inserted_ids(self):
- """deprecated. use :attr:`~ResultProxy.inserted_primary_key`."""
+ """Return the primary key for the row just inserted."""
return self.inserted_primary_key
diff --git a/lib/sqlalchemy/ext/declarative.py b/lib/sqlalchemy/ext/declarative.py
index 2310f01ce..e40ba3ec4 100755
--- a/lib/sqlalchemy/ext/declarative.py
+++ b/lib/sqlalchemy/ext/declarative.py
@@ -1197,7 +1197,7 @@ def _deferred_relationship(cls, prop):
if isinstance(prop, RelationshipProperty):
for attr in ('argument', 'order_by', 'primaryjoin', 'secondaryjoin',
- 'secondary', '_foreign_keys', 'remote_side'):
+ 'secondary', '_user_defined_foreign_keys', 'remote_side'):
v = getattr(prop, attr)
if isinstance(v, basestring):
setattr(prop, attr, resolve_arg(v))
diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py
index 2004dccd1..c74fabacd 100644
--- a/lib/sqlalchemy/orm/__init__.py
+++ b/lib/sqlalchemy/orm/__init__.py
@@ -274,15 +274,24 @@ def relationship(argument, secondary=None, **kwargs):
:param foreign_keys:
a list of columns which are to be used as "foreign key" columns.
- this parameter should be used in conjunction with explicit
- ``primaryjoin`` and ``secondaryjoin`` (if needed) arguments, and
- the columns within the ``foreign_keys`` list should be present
- within those join conditions. Normally, ``relationship()`` will
- inspect the columns within the join conditions to determine
- which columns are the "foreign key" columns, based on
- information in the ``Table`` metadata. Use this argument when no
- ForeignKey's are present in the join condition, or to override
- the table-defined foreign keys.
+ Normally, :func:`relationship` uses the :class:`.ForeignKey`
+ and :class:`.ForeignKeyConstraint` objects present within the
+ mapped or secondary :class:`.Table` to determine the "foreign" side of
+ the join condition. This is used to construct SQL clauses in order
+ to load objects, as well as to "synchronize" values from
+ primary key columns to referencing foreign key columns.
+ The ``foreign_keys`` parameter overrides the notion of what's
+ "foreign" in the table metadata, allowing the specification
+ of a list of :class:`.Column` objects that should be considered
+ part of the foreign key.
+
+ There are only two use cases for ``foreign_keys`` - one, when it is not
+ convenient for :class:`.Table` metadata to contain its own foreign key
+ metadata (which should be almost never, unless reflecting a large amount of
+ tables from a MySQL MyISAM schema, or a schema that doesn't actually
+ have foreign keys on it). The other is for extremely
+ rare and exotic composite foreign key setups where some columns
+ should artificially not be considered as foreign.
:param innerjoin=False:
when ``True``, joined eager loads will use an inner join to join
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index b268cf6b5..ddb08039a 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -573,6 +573,13 @@ class Mapper(object):
if col is None:
instrument = False
col = self.polymorphic_on
+ if self.with_polymorphic is None \
+ or self.with_polymorphic[1].corresponding_column(col) \
+ is None:
+ util.warn("Could not map polymorphic_on column "
+ "'%s' to the mapped table - polymorphic "
+ "loads will not function properly"
+ % col.description)
else:
instrument = True
if self._should_exclude(col.key, col.key, local=False):
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py
index cbfba91f3..5788c30f9 100644
--- a/lib/sqlalchemy/orm/properties.py
+++ b/lib/sqlalchemy/orm/properties.py
@@ -446,7 +446,7 @@ class RelationshipProperty(StrategizedProperty):
self.viewonly = viewonly
self.lazy = lazy
self.single_parent = single_parent
- self._foreign_keys = foreign_keys
+ self._user_defined_foreign_keys = foreign_keys
self.collection_class = collection_class
self.passive_deletes = passive_deletes
self.passive_updates = passive_updates
@@ -695,7 +695,7 @@ class RelationshipProperty(StrategizedProperty):
if isinstance(other, (NoneType, expression._Null)):
if self.property.direction == MANYTOONE:
return sql.or_(*[x != None for x in
- self.property._foreign_keys])
+ self.property._calculated_foreign_keys])
else:
return self._criterion_exists()
elif self.property.uselist:
@@ -912,7 +912,7 @@ class RelationshipProperty(StrategizedProperty):
'primaryjoin',
'secondaryjoin',
'secondary',
- '_foreign_keys',
+ '_user_defined_foreign_keys',
'remote_side',
):
if util.callable(getattr(self, attr)):
@@ -931,9 +931,9 @@ class RelationshipProperty(StrategizedProperty):
if self.order_by is not False and self.order_by is not None:
self.order_by = [expression._literal_as_column(x) for x in
util.to_list(self.order_by)]
- self._foreign_keys = \
+ self._user_defined_foreign_keys = \
util.column_set(expression._literal_as_column(x) for x in
- util.to_column_set(self._foreign_keys))
+ util.to_column_set(self._user_defined_foreign_keys))
self.remote_side = \
util.column_set(expression._literal_as_column(x) for x in
util.to_column_set(self.remote_side))
@@ -999,8 +999,8 @@ class RelationshipProperty(StrategizedProperty):
raise sa_exc.ArgumentError("Could not determine join "
"condition between parent/child tables on "
"relationship %s. Specify a 'primaryjoin' "
- "expression. If this is a many-to-many "
- "relationship, 'secondaryjoin' is needed as well."
+ "expression. If 'secondary' is present, "
+ "'secondaryjoin' is needed as well."
% self)
def _col_is_part_of_mappings(self, column):
@@ -1012,91 +1012,121 @@ class RelationshipProperty(StrategizedProperty):
self.target.c.contains_column(column) or \
self.secondary.c.contains_column(column) is not None
+ def _sync_pairs_from_join(self, join_condition, primary):
+ """Given a join condition, figure out what columns are foreign
+ and are part of a binary "equated" condition to their referecned
+ columns, and convert into a list of tuples of (primary col->foreign col).
+
+ Make several attempts to determine if cols are compared using
+ "=" or other comparators (in which case suggest viewonly),
+ columns are present but not part of the expected mappings, columns
+ don't have any :class:`ForeignKey` information on them, or
+ the ``foreign_keys`` attribute is being used incorrectly.
+
+ """
+ eq_pairs = criterion_as_pairs(join_condition,
+ consider_as_foreign_keys=self._user_defined_foreign_keys,
+ any_operator=self.viewonly)
+
+ eq_pairs = [(l, r) for (l, r) in eq_pairs
+ if self._col_is_part_of_mappings(l)
+ and self._col_is_part_of_mappings(r)
+ or self.viewonly and r in self._user_defined_foreign_keys]
+
+ if not eq_pairs and \
+ self.secondary is not None and \
+ not self._user_defined_foreign_keys:
+ fks = set(self.secondary.c)
+ eq_pairs = criterion_as_pairs(join_condition,
+ consider_as_foreign_keys=fks,
+ any_operator=self.viewonly)
+
+ eq_pairs = [(l, r) for (l, r) in eq_pairs
+ if self._col_is_part_of_mappings(l)
+ and self._col_is_part_of_mappings(r)
+ or self.viewonly and r in fks]
+ if eq_pairs:
+ util.warn("No ForeignKey objects were present "
+ "in secondary table '%s'. Assumed referenced "
+ "foreign key columns %s for join condition '%s' "
+ "on relationship %s" % (
+ self.secondary.description,
+ ", ".join(sorted(["'%s'" % col for col in fks])),
+ join_condition,
+ self
+ ))
+
+ if not eq_pairs:
+ if not self.viewonly and criterion_as_pairs(join_condition,
+ consider_as_foreign_keys=self._user_defined_foreign_keys,
+ any_operator=True):
+ raise sa_exc.ArgumentError("Could not locate any "
+ "equated, locally mapped column pairs for %s "
+ "condition '%s' on relationship %s. For more "
+ "relaxed rules on join conditions, the "
+ "relationship may be marked as viewonly=True."
+ % (
+ primary and 'primaryjoin' or 'secondaryjoin',
+ join_condition,
+ self
+ ))
+ else:
+ if self._user_defined_foreign_keys:
+ raise sa_exc.ArgumentError("Could not determine "
+ "relationship direction for %s condition "
+ "'%s', on relationship %s, using manual "
+ "'foreign_keys' setting. Do the columns "
+ "in 'foreign_keys' represent all, and "
+ "only, the 'foreign' columns in this join "
+ "condition? Does the %s Table already "
+ "have adequate ForeignKey and/or "
+ "ForeignKeyConstraint objects established "
+ "(in which case 'foreign_keys' is usually "
+ "unnecessary)?"
+ % (
+ primary and 'primaryjoin' or 'secondaryjoin',
+ join_condition,
+ self,
+ primary and 'mapped' or 'secondary'
+ ))
+ else:
+ raise sa_exc.ArgumentError("Could not determine "
+ "relationship direction for %s condition "
+ "'%s', on relationship %s. Ensure that the "
+ "referencing Column objects have a "
+ "ForeignKey present, or are otherwise part "
+ "of a ForeignKeyConstraint on their parent "
+ "Table."
+ % (
+ primary and 'primaryjoin' or 'secondaryjoin',
+ join_condition,
+ self
+ ))
+ return eq_pairs
+
def _determine_synchronize_pairs(self):
if self.local_remote_pairs:
- if not self._foreign_keys:
+ if not self._user_defined_foreign_keys:
raise sa_exc.ArgumentError('foreign_keys argument is '
'required with _local_remote_pairs argument')
self.synchronize_pairs = []
for l, r in self.local_remote_pairs:
- if r in self._foreign_keys:
+ if r in self._user_defined_foreign_keys:
self.synchronize_pairs.append((l, r))
- elif l in self._foreign_keys:
+ elif l in self._user_defined_foreign_keys:
self.synchronize_pairs.append((r, l))
else:
- eq_pairs = criterion_as_pairs(self.primaryjoin,
- consider_as_foreign_keys=self._foreign_keys,
- any_operator=self.viewonly)
- eq_pairs = [(l, r) for (l, r) in eq_pairs
- if self._col_is_part_of_mappings(l)
- and self._col_is_part_of_mappings(r)
- or self.viewonly and r in self._foreign_keys]
- if not eq_pairs:
- if not self.viewonly \
- and criterion_as_pairs(self.primaryjoin,
- consider_as_foreign_keys=self._foreign_keys,
- any_operator=True):
- raise sa_exc.ArgumentError("Could not locate any "
- "equated, locally mapped column pairs for "
- "primaryjoin condition '%s' on "
- "relationship %s. For more relaxed rules "
- "on join conditions, the relationship may "
- "be marked as viewonly=True."
- % (self.primaryjoin, self))
- else:
- if self._foreign_keys:
- raise sa_exc.ArgumentError("Could not determine"
- " relationship direction for "
- "primaryjoin condition '%s', on "
- "relationship %s. Do the columns in "
- "'foreign_keys' represent only the "
- "'foreign' columns in this join "
- "condition ?" % (self.primaryjoin,
- self))
- else:
- raise sa_exc.ArgumentError("Could not determine"
- " relationship direction for "
- "primaryjoin condition '%s', on "
- "relationship %s. Specify the "
- "'foreign_keys' argument to indicate "
- "which columns on the relationship are "
- "foreign." % (self.primaryjoin, self))
+ eq_pairs = self._sync_pairs_from_join(self.primaryjoin, True)
self.synchronize_pairs = eq_pairs
if self.secondaryjoin is not None:
- sq_pairs = criterion_as_pairs(self.secondaryjoin,
- consider_as_foreign_keys=self._foreign_keys,
- any_operator=self.viewonly)
- sq_pairs = [(l, r) for (l, r) in sq_pairs
- if self._col_is_part_of_mappings(l)
- and self._col_is_part_of_mappings(r) or r
- in self._foreign_keys]
- if not sq_pairs:
- if not self.viewonly \
- and criterion_as_pairs(self.secondaryjoin,
- consider_as_foreign_keys=self._foreign_keys,
- any_operator=True):
- raise sa_exc.ArgumentError("Could not locate any "
- "equated, locally mapped column pairs for "
- "secondaryjoin condition '%s' on "
- "relationship %s. For more relaxed rules "
- "on join conditions, the relationship may "
- "be marked as viewonly=True."
- % (self.secondaryjoin, self))
- else:
- raise sa_exc.ArgumentError("Could not determine "
- "relationship direction for secondaryjoin "
- "condition '%s', on relationship %s. "
- "Specify the foreign_keys argument to "
- "indicate which columns on the "
- "relationship are foreign."
- % (self.secondaryjoin, self))
+ sq_pairs = self._sync_pairs_from_join(self.secondaryjoin, False)
self.secondary_synchronize_pairs = sq_pairs
else:
self.secondary_synchronize_pairs = None
- self._foreign_keys = util.column_set(r for (l, r) in
+ self._calculated_foreign_keys = util.column_set(r for (l, r) in
self.synchronize_pairs)
if self.secondary_synchronize_pairs:
- self._foreign_keys.update(r for (l, r) in
+ self._calculated_foreign_keys.update(r for (l, r) in
self.secondary_synchronize_pairs)
def _determine_direction(self):
@@ -1114,7 +1144,7 @@ class RelationshipProperty(StrategizedProperty):
remote = self.remote_side
else:
remote = None
- if not remote or self._foreign_keys.difference(l for (l,
+ if not remote or self._calculated_foreign_keys.difference(l for (l,
r) in self.synchronize_pairs).intersection(remote):
self.direction = ONETOMANY
else:
@@ -1192,12 +1222,12 @@ class RelationshipProperty(StrategizedProperty):
eq_pairs += self.secondary_synchronize_pairs
else:
eq_pairs = criterion_as_pairs(self.primaryjoin,
- consider_as_foreign_keys=self._foreign_keys,
+ consider_as_foreign_keys=self._calculated_foreign_keys,
any_operator=True)
if self.secondaryjoin is not None:
eq_pairs += \
criterion_as_pairs(self.secondaryjoin,
- consider_as_foreign_keys=self._foreign_keys,
+ consider_as_foreign_keys=self._calculated_foreign_keys,
any_operator=True)
eq_pairs = [(l, r) for (l, r) in eq_pairs
if self._col_is_part_of_mappings(l)
@@ -1266,7 +1296,7 @@ class RelationshipProperty(StrategizedProperty):
"a non-secondary relationship."
)
foreign_keys = kwargs.pop('foreign_keys',
- self._foreign_keys)
+ self._user_defined_foreign_keys)
parent = self.parent.primary_mapper()
kwargs.setdefault('viewonly', self.viewonly)
kwargs.setdefault('post_update', self.post_update)
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index c269c8bb9..c33687484 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -745,7 +745,9 @@ class Query(object):
# given arg is a FROM clause
self._setup_aliasizers(self._entities[l:])
- @util.pending_deprecation("0.7", "add_column() is superceded by add_columns()", False)
+ @util.pending_deprecation("0.7",
+ ":meth:`.add_column` is superceded by :meth:`.add_columns`",
+ False)
def add_column(self, column):
"""Add a column expression to the list of result columns to be returned.
diff --git a/lib/sqlalchemy/orm/scoping.py b/lib/sqlalchemy/orm/scoping.py
index 40bbb3299..af518e407 100644
--- a/lib/sqlalchemy/orm/scoping.py
+++ b/lib/sqlalchemy/orm/scoping.py
@@ -58,13 +58,11 @@ class ScopedSession(object):
self.registry().close()
self.registry.clear()
- @deprecated("Session.mapper is deprecated. "
+ @deprecated("0.5", ":meth:`.ScopedSession.mapper` is deprecated. "
"Please see http://www.sqlalchemy.org/trac/wiki/UsageRecipes/SessionAwareMapper "
"for information on how to replicate its behavior.")
def mapper(self, *args, **kwargs):
- """return a mapper() function which associates this ScopedSession with the Mapper.
-
- DEPRECATED.
+ """return a :func:`.mapper` function which associates this ScopedSession with the Mapper.
"""
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index 8516a22de..c67dc5553 100644
--- a/lib/sqlalchemy/orm/session.py
+++ b/lib/sqlalchemy/orm/session.py
@@ -918,6 +918,13 @@ class Session(object):
Eagerly-loaded relational attributes will eagerly load within the
single refresh operation.
+ Note that a highly isolated transaction will return the same values as
+ were previously read in that same transaction, regardless of changes
+ in database state outside of that transaction - usage of
+ :meth:`~Session.refresh` usually only makes sense if non-ORM SQL
+ statement were emitted in the ongoing transaction, or if autocommit
+ mode is turned on.
+
:param attribute_names: optional. An iterable collection of
string attribute names indicating a subset of attributes to
be refreshed.
@@ -942,22 +949,55 @@ class Session(object):
mapperutil.instance_str(instance))
def expire_all(self):
- """Expires all persistent instances within this Session."""
+ """Expires all persistent instances within this Session.
+
+ When any attributes on a persitent instance is next accessed,
+ a query will be issued using the
+ :class:`.Session` object's current transactional context in order to
+ load all expired attributes for the given instance. Note that
+ a highly isolated transaction will return the same values as were
+ previously read in that same transaction, regardless of changes
+ in database state outside of that transaction.
+
+ To expire individual objects and individual attributes
+ on those objects, use :meth:`Session.expire`.
+
+ The :class:`Session` object's default behavior is to
+ expire all state whenever the :meth:`Session.rollback`
+ or :meth:`Session.commit` methods are called, so that new
+ state can be loaded for the new transaction. For this reason,
+ calling :meth:`Session.expire_all` should not be needed when
+ autocommit is ``False``, assuming the transaction is isolated.
+ """
for state in self.identity_map.all_states():
_expire_state(state, state.dict, None, instance_dict=self.identity_map)
def expire(self, instance, attribute_names=None):
"""Expire the attributes on an instance.
- Marks the attributes of an instance as out of date. When an expired
- attribute is next accessed, query will be issued to the database and
- the attributes will be refreshed with their current database value.
- ``expire()`` is a lazy variant of ``refresh()``.
+ Marks the attributes of an instance as out of date. When an expired
+ attribute is next accessed, a query will be issued to the
+ :class:`.Session` object's current transactional context in order to
+ load all expired attributes for the given instance. Note that
+ a highly isolated transaction will return the same values as were
+ previously read in that same transaction, regardless of changes
+ in database state outside of that transaction.
+
+ To expire all objects in the :class:`.Session` simultaneously,
+ use :meth:`Session.expire_all`.
+
+ The :class:`Session` object's default behavior is to
+ expire all state whenever the :meth:`Session.rollback`
+ or :meth:`Session.commit` methods are called, so that new
+ state can be loaded for the new transaction. For this reason,
+ calling :meth:`Session.expire` only makes sense for the specific
+ case that a non-ORM SQL statement was emitted in the current
+ transaction.
- The ``attribute_names`` argument is an iterable collection
- of attribute names indicating a subset of attributes to be
- expired.
+ :param instance: The instance to be refreshed.
+ :param attribute_names: optional list of string attribute names
+ indicating a subset of attributes to be expired.
"""
try:
diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py
index 5f82a0200..5b5983858 100644
--- a/lib/sqlalchemy/schema.py
+++ b/lib/sqlalchemy/schema.py
@@ -1405,9 +1405,16 @@ class DefaultClause(FetchedValue):
(self.arg, self.for_update)
class PassiveDefault(DefaultClause):
+ """A DDL-specified DEFAULT column value.
+
+ .. deprecated:: 0.6 :class:`.PassiveDefault` is deprecated.
+ Use :class:`.DefaultClause`.
+ """
+ @util.deprecated("0.6",
+ ":class:`.PassiveDefault` is deprecated. "
+ "Use :class:`.DefaultClause`.",
+ False)
def __init__(self, *arg, **kw):
- util.warn_deprecated("PassiveDefault is deprecated. "
- "Use DefaultClause.")
DefaultClause.__init__(self, *arg, **kw)
class Constraint(SchemaItem):
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index 0a5edb42f..a7f5d396a 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -38,16 +38,15 @@ functions, sql_util, sqltypes = None, None, None
DefaultDialect = None
__all__ = [
- 'Alias', 'ClauseElement',
- 'ColumnCollection', 'ColumnElement',
- 'CompoundSelect', 'Delete', 'FromClause', 'Insert', 'Join',
- 'Select', 'Selectable', 'TableClause', 'Update', 'alias', 'and_', 'asc',
- 'between', 'bindparam', 'case', 'cast', 'column', 'delete',
- 'desc', 'distinct', 'except_', 'except_all', 'exists', 'extract', 'func',
- 'modifier', 'collate',
- 'insert', 'intersect', 'intersect_all', 'join', 'label', 'literal',
- 'literal_column', 'not_', 'null', 'or_', 'outparam', 'outerjoin', 'select',
- 'subquery', 'table', 'text', 'tuple_', 'union', 'union_all', 'update', ]
+ 'Alias', 'ClauseElement', 'ColumnCollection', 'ColumnElement',
+ 'CompoundSelect', 'Delete', 'FromClause', 'Insert', 'Join', 'Select',
+ 'Selectable', 'TableClause', 'Update', 'alias', 'and_', 'asc', 'between',
+ 'bindparam', 'case', 'cast', 'column', 'delete', 'desc', 'distinct',
+ 'except_', 'except_all', 'exists', 'extract', 'func', 'modifier',
+ 'collate', 'insert', 'intersect', 'intersect_all', 'join', 'label',
+ 'literal', 'literal_column', 'not_', 'null', 'or_', 'outparam',
+ 'outerjoin', 'select', 'subquery', 'table', 'text', 'tuple_', 'union',
+ 'union_all', 'update', ]
PARSE_AUTOCOMMIT = util._symbol('PARSE_AUTOCOMMIT')
@@ -133,108 +132,108 @@ def select(columns=None, whereclause=None, from_obj=[], **kwargs):
string arguments, which will be converted as appropriate into
either :func:`text()` or :func:`literal_column()` constructs.
- columns
- A list of :class:`ClauseElement` objects, typically :class:`ColumnElement`
- objects or subclasses, which will form the columns clause of the
- resulting statement. For all members which are instances of
- :class:`Selectable`, the individual :class:`ColumnElement` members of the
- :class:`Selectable` will be added individually to the columns clause.
- For example, specifying a :class:`~sqlalchemy.schema.Table` instance will result in all
- the contained :class:`~sqlalchemy.schema.Column` objects within to be added to the
- columns clause.
+ :param columns:
+ A list of :class:`ClauseElement` objects, typically
+ :class:`ColumnElement` objects or subclasses, which will form the
+ columns clause of the resulting statement. For all members which are
+ instances of :class:`Selectable`, the individual :class:`ColumnElement`
+ members of the :class:`Selectable` will be added individually to the
+ columns clause. For example, specifying a
+ :class:`~sqlalchemy.schema.Table` instance will result in all the
+ contained :class:`~sqlalchemy.schema.Column` objects within to be added
+ to the columns clause.
This argument is not present on the form of :func:`select()`
available on :class:`~sqlalchemy.schema.Table`.
- whereclause
+ :param whereclause:
A :class:`ClauseElement` expression which will be used to form the
``WHERE`` clause.
- from_obj
+ :param from_obj:
A list of :class:`ClauseElement` objects which will be added to the
- ``FROM`` clause of the resulting statement. Note that "from"
- objects are automatically located within the columns and
- whereclause ClauseElements. Use this parameter to explicitly
- specify "from" objects which are not automatically locatable.
- This could include :class:`~sqlalchemy.schema.Table` objects that aren't otherwise
- present, or :class:`Join` objects whose presence will supercede that
- of the :class:`~sqlalchemy.schema.Table` objects already located in the other clauses.
+ ``FROM`` clause of the resulting statement. Note that "from" objects are
+ automatically located within the columns and whereclause ClauseElements.
+ Use this parameter to explicitly specify "from" objects which are not
+ automatically locatable. This could include
+ :class:`~sqlalchemy.schema.Table` objects that aren't otherwise present,
+ or :class:`Join` objects whose presence will supercede that of the
+ :class:`~sqlalchemy.schema.Table` objects already located in the other
+ clauses.
- \**kwargs
- Additional parameters include:
-
- autocommit
- Deprecated. Use .execution_options(autocommit=<True|False>)
- to set the autocommit option.
-
- prefixes
- a list of strings or :class:`ClauseElement` objects to include
- directly after the SELECT keyword in the generated statement,
- for dialect-specific query features.
-
- distinct=False
- when ``True``, applies a ``DISTINCT`` qualifier to the columns
- clause of the resulting statement.
-
- use_labels=False
- when ``True``, the statement will be generated using labels
- for each column in the columns clause, which qualify each
- column with its parent table's (or aliases) name so that name
- conflicts between columns in different tables don't occur.
- The format of the label is <tablename>_<column>. The "c"
- collection of the resulting :class:`Select` object will use these
- names as well for targeting column members.
-
- for_update=False
- when ``True``, applies ``FOR UPDATE`` to the end of the
- resulting statement. Certain database dialects also support
- alternate values for this parameter, for example mysql
- supports "read" which translates to ``LOCK IN SHARE MODE``,
- and oracle supports "nowait" which translates to ``FOR UPDATE
- NOWAIT``.
-
- correlate=True
- indicates that this :class:`Select` object should have its
- contained :class:`FromClause` elements "correlated" to an enclosing
- :class:`Select` object. This means that any :class:`ClauseElement`
- instance within the "froms" collection of this :class:`Select`
- which is also present in the "froms" collection of an
- enclosing select will not be rendered in the ``FROM`` clause
- of this select statement.
-
- group_by
- a list of :class:`ClauseElement` objects which will comprise the
- ``GROUP BY`` clause of the resulting select.
-
- having
- a :class:`ClauseElement` that will comprise the ``HAVING`` clause
- of the resulting select when ``GROUP BY`` is used.
-
- order_by
- a scalar or list of :class:`ClauseElement` objects which will
- comprise the ``ORDER BY`` clause of the resulting select.
-
- limit=None
- a numerical value which usually compiles to a ``LIMIT``
- expression in the resulting select. Databases that don't
- support ``LIMIT`` will attempt to provide similar
- functionality.
-
- offset=None
- a numeric value which usually compiles to an ``OFFSET``
- expression in the resulting select. Databases that don't
- support ``OFFSET`` will attempt to provide similar
- functionality.
-
- bind=None
- an ``Engine`` or ``Connection`` instance to which the
- resulting ``Select ` object will be bound. The ``Select``
- object will otherwise automatically bind to whatever
- ``Connectable`` instances can be located within its contained
- :class:`ClauseElement` members.
+ :param autocommit:
+ Deprecated. Use .execution_options(autocommit=<True|False>)
+ to set the autocommit option.
+
+ :param prefixes:
+ a list of strings or :class:`ClauseElement` objects to include
+ directly after the SELECT keyword in the generated statement,
+ for dialect-specific query features.
+
+ :param distinct=False:
+ when ``True``, applies a ``DISTINCT`` qualifier to the columns
+ clause of the resulting statement.
+
+ :param use_labels=False:
+ when ``True``, the statement will be generated using labels
+ for each column in the columns clause, which qualify each
+ column with its parent table's (or aliases) name so that name
+ conflicts between columns in different tables don't occur.
+ The format of the label is <tablename>_<column>. The "c"
+ collection of the resulting :class:`Select` object will use these
+ names as well for targeting column members.
+
+ :param for_update=False:
+ when ``True``, applies ``FOR UPDATE`` to the end of the
+ resulting statement. Certain database dialects also support
+ alternate values for this parameter, for example mysql
+ supports "read" which translates to ``LOCK IN SHARE MODE``,
+ and oracle supports "nowait" which translates to ``FOR UPDATE
+ NOWAIT``.
+
+ :param correlate=True:
+ indicates that this :class:`Select` object should have its
+ contained :class:`FromClause` elements "correlated" to an enclosing
+ :class:`Select` object. This means that any :class:`ClauseElement`
+ instance within the "froms" collection of this :class:`Select`
+ which is also present in the "froms" collection of an
+ enclosing select will not be rendered in the ``FROM`` clause
+ of this select statement.
+
+ :param group_by:
+ a list of :class:`ClauseElement` objects which will comprise the
+ ``GROUP BY`` clause of the resulting select.
+
+ :param having:
+ a :class:`ClauseElement` that will comprise the ``HAVING`` clause
+ of the resulting select when ``GROUP BY`` is used.
+
+ :param order_by:
+ a scalar or list of :class:`ClauseElement` objects which will
+ comprise the ``ORDER BY`` clause of the resulting select.
+
+ :param limit=None:
+ a numerical value which usually compiles to a ``LIMIT``
+ expression in the resulting select. Databases that don't
+ support ``LIMIT`` will attempt to provide similar
+ functionality.
+
+ :param offset=None:
+ a numeric value which usually compiles to an ``OFFSET``
+ expression in the resulting select. Databases that don't
+ support ``OFFSET`` will attempt to provide similar
+ functionality.
+
+ :param bind=None:
+ an ``Engine`` or ``Connection`` instance to which the
+ resulting ``Select ` object will be bound. The ``Select``
+ object will otherwise automatically bind to whatever
+ ``Connectable`` instances can be located within its contained
+ :class:`ClauseElement` members.
"""
- return Select(columns, whereclause=whereclause, from_obj=from_obj, **kwargs)
+ return Select(columns, whereclause=whereclause, from_obj=from_obj,
+ **kwargs)
def subquery(alias, *args, **kwargs):
"""Return an :class:`Alias` object derived
@@ -259,18 +258,17 @@ def insert(table, values=None, inline=False, **kwargs):
:param table: The table to be inserted into.
- :param values: A dictionary which specifies the column specifications of the
- ``INSERT``, and is optional. If left as None, the column
- specifications are determined from the bind parameters used
- during the compile phase of the ``INSERT`` statement. If the
- bind parameters also are None during the compile phase, then the
- column specifications will be generated from the full list of
- table columns. Note that the :meth:`~Insert.values()` generative method
- may also be used for this.
+ :param values: A dictionary which specifies the column specifications of
+ the ``INSERT``, and is optional. If left as None, the column
+ specifications are determined from the bind parameters used during the
+ compile phase of the ``INSERT`` statement. If the bind parameters also
+ are None during the compile phase, then the column specifications will be
+ generated from the full list of table columns. Note that the
+ :meth:`~Insert.values()` generative method may also be used for this.
:param prefixes: A list of modifier keywords to be inserted between INSERT
- and INTO. Alternatively, the :meth:`~Insert.prefix_with` generative method
- may be used.
+ and INTO. Alternatively, the :meth:`~Insert.prefix_with` generative
+ method may be used.
:param inline: if True, SQL defaults will be compiled 'inline' into the
statement and not pre-executed.
@@ -279,8 +277,8 @@ def insert(table, values=None, inline=False, **kwargs):
compile-time bind parameters override the information specified
within `values` on a per-key basis.
- The keys within `values` can be either :class:`~sqlalchemy.schema.Column` objects or their
- string identifiers. Each key may reference one of:
+ The keys within `values` can be either :class:`~sqlalchemy.schema.Column`
+ objects or their string identifiers. Each key may reference one of:
* a literal data value (i.e. string, number, etc.);
* a Column object;
@@ -301,9 +299,9 @@ def update(table, whereclause=None, values=None, inline=False, **kwargs):
:param table: The table to be updated.
- :param whereclause: A :class:`ClauseElement` describing the ``WHERE`` condition
- of the ``UPDATE`` statement. Note that the :meth:`~Update.where()`
- generative method may also be used for this.
+ :param whereclause: A :class:`ClauseElement` describing the ``WHERE``
+ condition of the ``UPDATE`` statement. Note that the
+ :meth:`~Update.where()` generative method may also be used for this.
:param values:
A dictionary which specifies the ``SET`` conditions of the
@@ -323,7 +321,8 @@ def update(table, whereclause=None, values=None, inline=False, **kwargs):
compile-time bind parameters override the information specified
within `values` on a per-key basis.
- The keys within `values` can be either :class:`~sqlalchemy.schema.Column` objects or their
+ The keys within `values` can be either :class:`~sqlalchemy.schema.Column`
+ objects or their
string identifiers. Each key may reference one of:
* a literal data value (i.e. string, number, etc.);
@@ -351,8 +350,8 @@ def delete(table, whereclause = None, **kwargs):
:param table: The table to be updated.
:param whereclause: A :class:`ClauseElement` describing the ``WHERE``
- condition of the ``UPDATE`` statement. Note that the :meth:`~Delete.where()`
- generative method may be used instead.
+ condition of the ``UPDATE`` statement. Note that the
+ :meth:`~Delete.where()` generative method may be used instead.
"""
return Delete(table, whereclause, **kwargs)
@@ -454,8 +453,10 @@ def case(whens, value=None, else_=None):
can be specified which determines the type of the :func:`case()` construct
overall::
- case([(orderline.c.qty > 100, literal_column("'greaterthan100'", String)),
- (orderline.c.qty > 10, literal_column("'greaterthan10'", String))
+ case([(orderline.c.qty > 100,
+ literal_column("'greaterthan100'", String)),
+ (orderline.c.qty > 10, literal_column("'greaterthan10'",
+ String))
], else_=literal_column("'lethan10'", String))
"""
@@ -917,8 +918,8 @@ class _FunctionGenerator(object):
issubclass(func, Function):
return func(*c, **o)
- return Function(
- self.__names[-1], packagenames=self.__names[0:-1], *c, **o)
+ return Function(self.__names[-1],
+ packagenames=self.__names[0:-1], *c, **o)
# "func" global - i.e. func.count()
func = _FunctionGenerator()
@@ -961,7 +962,8 @@ def _cloned_intersection(a, b):
"""
all_overlap = set(_expand_cloned(a)).intersection(_expand_cloned(b))
- return set(elem for elem in a if all_overlap.intersection(elem._cloned_set))
+ return set(elem for elem in a
+ if all_overlap.intersection(elem._cloned_set))
def _is_literal(element):
@@ -1043,7 +1045,8 @@ def _only_column_elements(element):
"got: %r" % element)
return element
-def _corresponding_column_or_error(fromclause, column, require_embedded=False):
+def _corresponding_column_or_error(fromclause, column,
+ require_embedded=False):
c = fromclause.corresponding_column(column,
require_embedded=require_embedded)
if c is None:
@@ -1282,8 +1285,8 @@ class ClauseElement(Visitable):
return e._execute_clauseelement(self, multiparams, params)
def scalar(self, *multiparams, **params):
- """Compile and execute this :class:`ClauseElement`, returning the result's
- scalar representation.
+ """Compile and execute this :class:`ClauseElement`, returning the
+ result's scalar representation.
"""
return self.execute(*multiparams, **params).scalar()
@@ -1309,7 +1312,8 @@ class ClauseElement(Visitable):
:param dialect: A ``Dialect`` instance frmo which a ``Compiled``
will be acquired. This argument takes precedence over the `bind`
- argument as well as this :class:`ClauseElement`'s bound engine, if any.
+ argument as well as this :class:`ClauseElement`'s bound engine, if
+ any.
:param inline: Used for INSERT statements, for a dialect which does
not support inline retrieval of newly generated primary key
@@ -1337,7 +1341,8 @@ class ClauseElement(Visitable):
return compiler
def _compiler(self, dialect, **kw):
- """Return a compiler appropriate for this ClauseElement, given a Dialect."""
+ """Return a compiler appropriate for this ClauseElement, given a
+ Dialect."""
return dialect.statement_compiler(dialect, self, **kw)
@@ -1513,16 +1518,22 @@ class ColumnOperators(Operators):
return self.reverse_operate(operators.truediv, other)
class _CompareMixin(ColumnOperators):
- """Defines comparison and math operations for :class:`ClauseElement` instances."""
+ """Defines comparison and math operations for :class:`ClauseElement`
+ instances."""
- def __compare(self, op, obj, negate=None, reverse=False, **kwargs):
+ def __compare(self, op, obj, negate=None, reverse=False,
+ **kwargs
+ ):
if obj is None or isinstance(obj, _Null):
if op == operators.eq:
- return _BinaryExpression(self, null(), operators.is_, negate=operators.isnot)
+ return _BinaryExpression(self, null(), operators.is_,
+ negate=operators.isnot)
elif op == operators.ne:
- return _BinaryExpression(self, null(), operators.isnot, negate=operators.is_)
+ return _BinaryExpression(self, null(), operators.isnot,
+ negate=operators.is_)
else:
- raise exc.ArgumentError("Only '='/'!=' operators can be used with NULL")
+ raise exc.ArgumentError("Only '='/'!=' operators can "
+ "be used with NULL")
else:
obj = self._check_literal(op, obj)
@@ -1548,12 +1559,14 @@ class _CompareMixin(ColumnOperators):
left, right = self, obj
if left.type is None:
- op, result_type = sqltypes.NULLTYPE._adapt_expression(op, right.type)
+ op, result_type = sqltypes.NULLTYPE._adapt_expression(op,
+ right.type)
elif right.type is None:
- op, result_type = left.type._adapt_expression(op, sqltypes.NULLTYPE)
- else:
- op, result_type = left.type._adapt_expression(op, right.type)
-
+ op, result_type = left.type._adapt_expression(op,
+ sqltypes.NULLTYPE)
+ else:
+ op, result_type = left.type._adapt_expression(op,
+ right.type)
return _BinaryExpression(left, right, op, type_=result_type)
@@ -1593,43 +1606,52 @@ class _CompareMixin(ColumnOperators):
seq_or_selectable = _clause_element_as_expr(seq_or_selectable)
if isinstance(seq_or_selectable, _ScalarSelect):
- return self.__compare( op, seq_or_selectable, negate=negate_op)
-
+ return self.__compare(op, seq_or_selectable,
+ negate=negate_op)
elif isinstance(seq_or_selectable, _SelectBaseMixin):
- # TODO: if we ever want to support (x, y, z) IN (select x, y, z from table),
- # we would need a multi-column version of as_scalar() to produce a multi-
- # column selectable that does not export itself as a FROM clause
- return self.__compare( op, seq_or_selectable.as_scalar(), negate=negate_op)
+ # TODO: if we ever want to support (x, y, z) IN (select x,
+ # y, z from table), we would need a multi-column version of
+ # as_scalar() to produce a multi- column selectable that
+ # does not export itself as a FROM clause
+
+ return self.__compare(op, seq_or_selectable.as_scalar(),
+ negate=negate_op)
elif isinstance(seq_or_selectable, (Selectable, _TextClause)):
- return self.__compare( op, seq_or_selectable, negate=negate_op)
+ return self.__compare(op, seq_or_selectable,
+ negate=negate_op)
# Handle non selectable arguments as sequences
+
args = []
for o in seq_or_selectable:
if not _is_literal(o):
- if not isinstance( o, _CompareMixin):
- raise exc.InvalidRequestError(
- "in() function accepts either a list of non-selectable values, "
- "or a selectable: %r" % o)
+ if not isinstance(o, _CompareMixin):
+ raise exc.InvalidRequestError('in() function accept'
+ 's either a list of non-selectable values, '
+ 'or a selectable: %r' % o)
else:
o = self._bind_param(op, o)
args.append(o)
-
if len(args) == 0:
- # Special case handling for empty IN's, behave like comparison
- # against zero row selectable. We use != to build the
- # contradiction as it handles NULL values appropriately, i.e.
- # "not (x IN ())" should not return NULL values for x.
- util.warn("The IN-predicate on \"%s\" was invoked with an empty sequence. "
- "This results in a contradiction, which nonetheless can be "
- "expensive to evaluate. Consider alternative strategies for "
- "improved performance." % self)
-
+
+ # Special case handling for empty IN's, behave like
+ # comparison against zero row selectable. We use != to
+ # build the contradiction as it handles NULL values
+ # appropriately, i.e. "not (x IN ())" should not return NULL
+ # values for x.
+
+ util.warn('The IN-predicate on "%s" was invoked with an '
+ 'empty sequence. This results in a '
+ 'contradiction, which nonetheless can be '
+ 'expensive to evaluate. Consider alternative '
+ 'strategies for improved performance.' % self)
return self != self
- return self.__compare(op, ClauseList(*args).self_group(against=op), negate=negate_op)
+ return self.__compare(op,
+ ClauseList(*args).self_group(against=op),
+ negate=negate_op)
def __neg__(self):
return _UnaryExpression(self, operator=operators.neg)
@@ -1670,7 +1692,9 @@ class _CompareMixin(ColumnOperators):
The allowed contents of ``other`` are database backend specific.
"""
- return self.__compare(operators.match_op, self._check_literal(operators.match_op, other))
+ return self.__compare(operators.match_op,
+ self._check_literal(operators.match_op,
+ other))
def label(self, name):
"""Produce a column label, i.e. ``<columnname> AS <name>``.
@@ -1692,10 +1716,13 @@ class _CompareMixin(ColumnOperators):
def distinct(self):
"""Produce a DISTINCT clause, i.e. ``DISTINCT <columnname>``"""
- return _UnaryExpression(self, operator=operators.distinct_op, type_=self.type)
+
+ return _UnaryExpression(self, operator=operators.distinct_op,
+ type_=self.type)
def between(self, cleft, cright):
- """Produce a BETWEEN clause, i.e. ``<column> BETWEEN <cleft> AND <cright>``"""
+ """Produce a BETWEEN clause, i.e. ``<column> BETWEEN <cleft> AND
+ <cright>``"""
return _BinaryExpression(
self,
@@ -1722,12 +1749,12 @@ class _CompareMixin(ColumnOperators):
somecolumn * 5
-
- :param operator: a string which will be output as the infix operator between
- this :class:`ClauseElement` and the expression passed to the
+ :param operator: a string which will be output as the infix operator
+ between this :class:`ClauseElement` and the expression passed to the
generated function.
- This function can also be used to make bitwise operators explicit. For example::
+ This function can also be used to make bitwise operators explicit. For
+ example::
somecolumn.op('&')(0xff)
@@ -1760,7 +1787,8 @@ class _CompareMixin(ColumnOperators):
class ColumnElement(ClauseElement, _CompareMixin):
- """Represent an element that is usable within the "column clause" portion of a ``SELECT`` statement.
+ """Represent an element that is usable within the "column clause" portion
+ of a ``SELECT`` statement.
This includes columns associated with tables, aliases, and
subqueries, expressions, function calls, SQL keywords such as
@@ -1817,10 +1845,12 @@ class ColumnElement(ClauseElement, _CompareMixin):
"""
if name:
- co = ColumnClause(name, selectable, type_=getattr(self, 'type', None))
+ co = ColumnClause(name, selectable, type_=getattr(self,
+ 'type', None))
else:
name = str(self)
- co = ColumnClause(self.anon_label, selectable, type_=getattr(self, 'type', None))
+ co = ColumnClause(self.anon_label, selectable,
+ type_=getattr(self, 'type', None))
co.proxies = [self]
selectable.columns[name] = co
@@ -1835,11 +1865,11 @@ class ColumnElement(ClauseElement, _CompareMixin):
share a common base column as equivalent (i.e. shares_lineage())
:param equivalents: a dictionary of columns as keys mapped to sets
- of columns. If the given "other" column is present in this dictionary,
- if any of the columns in the correponding set() pass the comparison
- test, the result is True. This is used to expand the comparison to
- other columns that may be known to be equivalent to this one via
- foreign key or other criterion.
+ of columns. If the given "other" column is present in this
+ dictionary, if any of the columns in the correponding set() pass the
+ comparison test, the result is True. This is used to expand the
+ comparison to other columns that may be known to be equivalent to
+ this one via foreign key or other criterion.
"""
to_compare = (other, )
@@ -1868,7 +1898,8 @@ class ColumnElement(ClauseElement, _CompareMixin):
expressions and function calls.
"""
- return _generated_label("%%(%d %s)s" % (id(self), getattr(self, 'name', 'anon')))
+ return _generated_label('%%(%d %s)s' % (id(self), getattr(self,
+ 'name', 'anon')))
class ColumnCollection(util.OrderedProperties):
"""An ordered dictionary that stores a list of ColumnElement
@@ -1919,13 +1950,17 @@ class ColumnCollection(util.OrderedProperties):
def __setitem__(self, key, value):
if key in self:
- # this warning is primarily to catch select() statements which
- # have conflicting column names in their exported columns collection
+
+ # this warning is primarily to catch select() statements
+ # which have conflicting column names in their exported
+ # columns collection
+
existing = self[key]
if not existing.shares_lineage(value):
- util.warn(("Column %r on table %r being replaced by another "
- "column with the same key. Consider use_labels "
- "for select() statements.") % (key, getattr(existing, 'table', None)))
+ util.warn('Column %r on table %r being replaced by '
+ 'another column with the same key. Consider '
+ 'use_labels for select() statements.' % (key,
+ getattr(existing, 'table', None)))
util.OrderedProperties.__setitem__(self, key, value)
def remove(self, column):
@@ -1951,9 +1986,11 @@ class ColumnCollection(util.OrderedProperties):
return util.OrderedProperties.__contains__(self, other)
def contains_column(self, col):
+
# have to use a Set here, because it will compare the identity
- # of the column, not just using "==" for comparison which will always return a
- # "True" value (i.e. a BinaryClause...)
+ # of the column, not just using "==" for comparison which will
+ # always return a "True" value (i.e. a BinaryClause...)
+
return col in util.column_set(self)
class ColumnSet(util.ordered_column_set):
@@ -1994,7 +2031,8 @@ class FromClause(Selectable):
schema = None
def count(self, whereclause=None, **params):
- """return a SELECT COUNT generated against this :class:`FromClause`."""
+ """return a SELECT COUNT generated against this
+ :class:`FromClause`."""
if self.primary_key:
col = list(self.primary_key)[0]
@@ -2012,12 +2050,14 @@ class FromClause(Selectable):
return select([self], whereclause, **params)
def join(self, right, onclause=None, isouter=False):
- """return a join of this :class:`FromClause` against another :class:`FromClause`."""
+ """return a join of this :class:`FromClause` against another
+ :class:`FromClause`."""
return Join(self, right, onclause, isouter)
def outerjoin(self, right, onclause=None):
- """return an outer join of this :class:`FromClause` against another :class:`FromClause`."""
+ """return an outer join of this :class:`FromClause` against another
+ :class:`FromClause`."""
return Join(self, right, onclause, True)
@@ -2041,7 +2081,8 @@ class FromClause(Selectable):
return Alias(self, name)
def is_derived_from(self, fromclause):
- """Return True if this FromClause is 'derived' from the given FromClause.
+ """Return True if this FromClause is 'derived' from the given
+ FromClause.
An example would be an Alias of a Table is derived from that Table.
@@ -2073,61 +2114,66 @@ class FromClause(Selectable):
return col
def corresponding_column(self, column, require_embedded=False):
- """Given a :class:`ColumnElement`, return the exported :class:`ColumnElement`
- object from this :class:`Selectable` which corresponds to that
- original :class:`~sqlalchemy.schema.Column` via a common anscestor column.
-
+ """Given a :class:`ColumnElement`, return the exported
+ :class:`ColumnElement` object from this :class:`Selectable`
+ which corresponds to that original
+ :class:`~sqlalchemy.schema.Column` via a common anscestor
+ column.
+
:param column: the target :class:`ColumnElement` to be matched
-
- :param require_embedded: only return corresponding columns for the given
- :class:`ColumnElement`, if the given :class:`ColumnElement` is
- actually present within a sub-element of this
- :class:`FromClause`. Normally the column will match if it merely
- shares a common anscestor with one of the exported columns
- of this :class:`FromClause`.
-
+
+ :param require_embedded: only return corresponding columns for
+ the given :class:`ColumnElement`, if the given
+ :class:`ColumnElement` is actually present within a sub-element
+ of this :class:`FromClause`. Normally the column will match if
+ it merely shares a common anscestor with one of the exported
+ columns of this :class:`FromClause`.
+
"""
+
# dont dig around if the column is locally present
+
if self.c.contains_column(column):
return column
-
col, intersect = None, None
target_set = column.proxy_set
cols = self.c
for c in cols:
- i = target_set.intersection(itertools.chain(*[p._cloned_set for p in c.proxy_set]))
-
- if i and \
- (not require_embedded or c.proxy_set.issuperset(target_set)):
-
+ i = target_set.intersection(itertools.chain(*[p._cloned_set
+ for p in c.proxy_set]))
+ if i and (not require_embedded
+ or c.proxy_set.issuperset(target_set)):
if col is None:
+
# no corresponding column yet, pick this one.
+
col, intersect = c, i
elif len(i) > len(intersect):
- # 'c' has a larger field of correspondence than 'col'.
- # i.e. selectable.c.a1_x->a1.c.x->table.c.x matches
- # a1.c.x->table.c.x better than
+
+ # 'c' has a larger field of correspondence than
+ # 'col'. i.e. selectable.c.a1_x->a1.c.x->table.c.x
+ # matches a1.c.x->table.c.x better than
# selectable.c.x->table.c.x does.
+
col, intersect = c, i
elif i == intersect:
- # they have the same field of correspondence.
- # see which proxy_set has fewer columns in it, which indicates
- # a closer relationship with the root column. Also take into
- # account the "weight" attribute which CompoundSelect() uses to
- # give higher precedence to columns based on vertical position
- # in the compound statement, and discard columns that have no
- # reference to the target column (also occurs with
- # CompoundSelect)
- col_distance = util.reduce(operator.add,
- [sc._annotations.get('weight', 1)
- for sc in col.proxy_set
- if sc.shares_lineage(column)]
- )
- c_distance = util.reduce(operator.add,
- [sc._annotations.get('weight', 1)
- for sc in c.proxy_set
- if sc.shares_lineage(column)]
- )
+
+ # they have the same field of correspondence. see
+ # which proxy_set has fewer columns in it, which
+ # indicates a closer relationship with the root
+ # column. Also take into account the "weight"
+ # attribute which CompoundSelect() uses to give
+ # higher precedence to columns based on vertical
+ # position in the compound statement, and discard
+ # columns that have no reference to the target
+ # column (also occurs with CompoundSelect)
+
+ col_distance = util.reduce(operator.add,
+ [sc._annotations.get('weight', 1) for sc in
+ col.proxy_set if sc.shares_lineage(column)])
+ c_distance = util.reduce(operator.add,
+ [sc._annotations.get('weight', 1) for sc in
+ c.proxy_set if sc.shares_lineage(column)])
if c_distance < col_distance:
col, intersect = c, i
return col
@@ -2144,12 +2190,14 @@ class FromClause(Selectable):
def _reset_exported(self):
"""delete memoized collections when a FromClause is cloned."""
- for attr in ('_columns', '_primary_key' '_foreign_keys', 'locate_all_froms'):
+ for attr in '_columns', '_primary_key_foreign_keys', \
+ 'locate_all_froms':
self.__dict__.pop(attr, None)
@util.memoized_property
def _columns(self):
- """Return the collection of Column objects contained by this FromClause."""
+ """Return the collection of Column objects contained by this
+ FromClause."""
self._export_columns()
return self._columns
@@ -2169,17 +2217,16 @@ class FromClause(Selectable):
self._export_columns()
return self._foreign_keys
-
columns = property(attrgetter('_columns'), doc=_columns.__doc__)
- primary_key = property(
- attrgetter('_primary_key'),
- doc=_primary_key.__doc__)
- foreign_keys = property(
- attrgetter('_foreign_keys'),
- doc=_foreign_keys.__doc__)
+ primary_key = property(attrgetter('_primary_key'),
+ doc=_primary_key.__doc__)
+ foreign_keys = property(attrgetter('_foreign_keys'),
+ doc=_foreign_keys.__doc__)
# synonyms for 'columns'
- c = _select_iterable = property(attrgetter('columns'), doc=_columns.__doc__)
+
+ c = _select_iterable = property(attrgetter('columns'),
+ doc=_columns.__doc__)
def _export_columns(self):
"""Initialize column collections."""
@@ -2208,7 +2255,7 @@ class _BindParamClause(ColumnElement):
_compared_to_type=None):
"""Construct a _BindParamClause.
- key
+ :param key:
the key for this bind param. Will be used in the generated
SQL statement for dialects that use named parameters. This
value may be modified when part of a compilation operation,
@@ -2216,45 +2263,49 @@ class _BindParamClause(ColumnElement):
key, or if its length is too long and truncation is
required.
- value
+ :param value:
Initial value for this bind param. This value may be
overridden by the dictionary of parameters sent to statement
compilation/execution.
- type\_
+ :param type\_:
A ``TypeEngine`` object that will be used to pre-process the
value corresponding to this :class:`_BindParamClause` at
execution time.
- unique
+ :param unique:
if True, the key name of this BindParamClause will be
modified if another :class:`_BindParamClause` of the same name
already has been located within the containing
:class:`ClauseElement`.
- required
+ :param required:
a value is required at execution time.
- isoutparam
- if True, the parameter should be treated like a stored procedure "OUT"
- parameter.
+ :param isoutparam:
+ if True, the parameter should be treated like a stored procedure
+ "OUT" parameter.
"""
if unique:
- self.key = _generated_label("%%(%d %s)s" % (id(self), key or 'param'))
+ self.key = _generated_label('%%(%d %s)s' % (id(self), key
+ or 'param'))
else:
- self.key = key or _generated_label("%%(%d param)s" % id(self))
+ self.key = key or _generated_label('%%(%d param)s'
+ % id(self))
self._orig_key = key or 'param'
self.unique = unique
self.value = value
self.isoutparam = isoutparam
self.required = required
-
if type_ is None:
if _compared_to_type is not None:
- self.type = _compared_to_type._coerce_compared_value(_compared_to_operator, value)
+ self.type = \
+ _compared_to_type._coerce_compared_value(
+ _compared_to_operator, value)
else:
- self.type = sqltypes.type_map.get(type(value), sqltypes.NULLTYPE)
+ self.type = sqltypes.type_map.get(type(value),
+ sqltypes.NULLTYPE)
elif isinstance(type_, type):
self.type = type_()
else:
@@ -2263,24 +2314,26 @@ class _BindParamClause(ColumnElement):
def _clone(self):
c = ClauseElement._clone(self)
if self.unique:
- c.key = _generated_label("%%(%d %s)s" % (id(c), c._orig_key or 'param'))
+ c.key = _generated_label('%%(%d %s)s' % (id(c), c._orig_key
+ or 'param'))
return c
def _convert_to_unique(self):
if not self.unique:
self.unique = True
- self.key = _generated_label("%%(%d %s)s" % (id(self),
- self._orig_key or 'param'))
+ self.key = _generated_label('%%(%d %s)s' % (id(self),
+ self._orig_key or 'param'))
def bind_processor(self, dialect):
return self.type.dialect_impl(dialect).bind_processor(dialect)
def compare(self, other, **kw):
- """Compare this :class:`_BindParamClause` to the given clause."""
-
- return isinstance(other, _BindParamClause) and \
- self.type._compare_type_affinity(other.type) and \
- self.value == other.value
+ """Compare this :class:`_BindParamClause` to the given
+ clause."""
+
+ return isinstance(other, _BindParamClause) \
+ and self.type._compare_type_affinity(other.type) \
+ and self.value == other.value
def __getstate__(self):
"""execute a deferred value for serialization purposes."""
@@ -2293,9 +2346,8 @@ class _BindParamClause(ColumnElement):
return d
def __repr__(self):
- return "_BindParamClause(%r, %r, type_=%r)" % (
- self.key, self.value, self.type
- )
+ return '_BindParamClause(%r, %r, type_=%r)' % (self.key,
+ self.value, self.type)
class _TypeClause(ClauseElement):
"""Handle a type keyword in a SQL statement.
@@ -2336,21 +2388,22 @@ class Executable(_Generative):
@_generative
def execution_options(self, **kw):
- """ Set non-SQL options for the statement which take effect during execution.
+ """ Set non-SQL options for the statement which take effect during
+ execution.
Current options include:
* autocommit - when True, a COMMIT will be invoked after execution
- when executed in 'autocommit' mode, i.e. when an explicit transaction
- is not begun on the connection. Note that DBAPI connections by
- default are always in a transaction - SQLAlchemy uses rules applied
- to different kinds of statements to determine if COMMIT will be invoked
- in order to provide its "autocommit" feature. Typically, all
- INSERT/UPDATE/DELETE statements as well as CREATE/DROP statements
- have autocommit behavior enabled; SELECT constructs do not. Use this
- option when invokving a SELECT or other specific SQL construct
- where COMMIT is desired (typically when calling stored procedures
- and such).
+ when executed in 'autocommit' mode, i.e. when an explicit
+ transaction is not begun on the connection. Note that DBAPI
+ connections by default are always in a transaction - SQLAlchemy uses
+ rules applied to different kinds of statements to determine if
+ COMMIT will be invoked in order to provide its "autocommit" feature.
+ Typically, all INSERT/UPDATE/DELETE statements as well as
+ CREATE/DROP statements have autocommit behavior enabled; SELECT
+ constructs do not. Use this option when invokving a SELECT or other
+ specific SQL construct where COMMIT is desired (typically when
+ calling stored procedures and such).
* stream_results - indicate to the dialect that results should be
"streamed" and not pre-buffered, if possible. This is a limitation
@@ -2396,7 +2449,9 @@ class _TextClause(Executable, ClauseElement):
__visit_name__ = 'textclause'
_bind_params_regex = re.compile(r'(?<![:\w\x5c]):(\w+)(?!:)', re.UNICODE)
- _execution_options = Executable._execution_options.union({'autocommit':PARSE_AUTOCOMMIT})
+ _execution_options = \
+ Executable._execution_options.union({'autocommit'
+ : PARSE_AUTOCOMMIT})
@property
def _select_iterable(self):
@@ -2404,28 +2459,35 @@ class _TextClause(Executable, ClauseElement):
_hide_froms = []
- def __init__(self, text = "", bind=None,
- bindparams=None, typemap=None,
- autocommit=None):
+ def __init__(
+ self,
+ text='',
+ bind=None,
+ bindparams=None,
+ typemap=None,
+ autocommit=None,
+ ):
self._bind = bind
self.bindparams = {}
self.typemap = typemap
-
if autocommit is not None:
- util.warn_deprecated("autocommit on text() is deprecated. "
- "Use .execution_options(autocommit=True)")
- self._execution_options = self._execution_options.union({'autocommit':autocommit})
-
+ util.warn_deprecated('autocommit on text() is deprecated. '
+ 'Use .execution_options(autocommit=Tru'
+ 'e)')
+ self._execution_options = \
+ self._execution_options.union({'autocommit'
+ : autocommit})
if typemap is not None:
for key in typemap.keys():
typemap[key] = sqltypes.to_instance(typemap[key])
def repl(m):
self.bindparams[m.group(1)] = bindparam(m.group(1))
- return ":%s" % m.group(1)
+ return ':%s' % m.group(1)
# scan the string and search for bind parameter names, add them
# to the list of bindparams
+
self.text = self._bind_params_regex.sub(repl, text)
if bindparams is not None:
for b in bindparams:
@@ -2509,7 +2571,8 @@ class ClauseList(ClauseElement):
# and a "flattened" ClauseList of ClauseLists. flatten()
# method ?
if self.group_contents:
- self.clauses.append(_literal_as_text(clause).self_group(against=self.operator))
+ self.clauses.append(_literal_as_text(clause).\
+ self_group(against=self.operator))
else:
self.clauses.append(_literal_as_text(clause))
@@ -2537,7 +2600,8 @@ class ClauseList(ClauseElement):
"""
if not isinstance(other, ClauseList) and len(self.clauses) == 1:
return self.clauses[0].compare(other, **kw)
- elif isinstance(other, ClauseList) and len(self.clauses) == len(other.clauses):
+ elif isinstance(other, ClauseList) and \
+ len(self.clauses) == len(other.clauses):
for i in range(0, len(self.clauses)):
if not self.clauses[i].compare(other.clauses[i], **kw):
return False
@@ -2551,7 +2615,8 @@ class BooleanClauseList(ClauseList, ColumnElement):
def __init__(self, *clauses, **kwargs):
super(BooleanClauseList, self).__init__(*clauses, **kwargs)
- self.type = sqltypes.to_instance(kwargs.get('type_', sqltypes.Boolean))
+ self.type = sqltypes.to_instance(kwargs.get('type_',
+ sqltypes.Boolean))
@property
def _select_iterable(self):
@@ -2587,11 +2652,13 @@ class _Case(ColumnElement):
if value is not None:
whenlist = [
- (_literal_as_binds(c).self_group(), _literal_as_binds(r)) for (c, r) in whens
+ (_literal_as_binds(c).self_group(),
+ _literal_as_binds(r)) for (c, r) in whens
]
else:
whenlist = [
- (_no_literals(c).self_group(), _literal_as_binds(r)) for (c, r) in whens
+ (_no_literals(c).self_group(),
+ _literal_as_binds(r)) for (c, r) in whens
]
if whenlist:
@@ -2629,7 +2696,8 @@ class _Case(ColumnElement):
@property
def _from_objects(self):
- return list(itertools.chain(*[x._from_objects for x in self.get_children()]))
+ return list(itertools.chain(*[x._from_objects for x in
+ self.get_children()]))
class FunctionElement(Executable, ColumnElement, FromClause):
"""Base for SQL function-oriented constructs."""
@@ -2692,8 +2760,10 @@ class Function(FunctionElement):
FunctionElement.__init__(self, *clauses, **kw)
def _bind_param(self, operator, obj):
- return _BindParamClause(self.name, obj, _compared_to_operator=operator,
- _compared_to_type=self.type, unique=True)
+ return _BindParamClause(self.name, obj,
+ _compared_to_operator=operator,
+ _compared_to_type=self.type,
+ unique=True)
class _Cast(ColumnElement):
@@ -2741,11 +2811,13 @@ class _UnaryExpression(ColumnElement):
__visit_name__ = 'unary'
- def __init__(self, element, operator=None, modifier=None, type_=None, negate=None):
+ def __init__(self, element, operator=None, modifier=None,
+ type_=None, negate=None):
self.operator = operator
self.modifier = modifier
- self.element = _literal_as_text(element).self_group(against=self.operator or self.modifier)
+ self.element = _literal_as_text(element).\
+ self_group(against=self.operator or self.modifier)
self.type = sqltypes.to_instance(type_)
self.negate = negate
@@ -2760,7 +2832,8 @@ class _UnaryExpression(ColumnElement):
return self.element,
def compare(self, other, **kw):
- """Compare this :class:`_UnaryExpression` against the given :class:`ClauseElement`."""
+ """Compare this :class:`_UnaryExpression` against the given
+ :class:`ClauseElement`."""
return (
isinstance(other, _UnaryExpression) and
@@ -2781,7 +2854,8 @@ class _UnaryExpression(ColumnElement):
return super(_UnaryExpression, self)._negate()
def self_group(self, against=None):
- if self.operator and operators.is_precedent(self.operator, against):
+ if self.operator and operators.is_precedent(self.operator,
+ against):
return _Grouping(self)
else:
return self
@@ -2792,7 +2866,8 @@ class _BinaryExpression(ColumnElement):
__visit_name__ = 'binary'
- def __init__(self, left, right, operator, type_=None, negate=None, modifiers=None):
+ def __init__(self, left, right, operator, type_=None,
+ negate=None, modifiers=None):
self.left = _literal_as_text(left).self_group(against=operator)
self.right = _literal_as_text(right).self_group(against=operator)
self.operator = operator
@@ -2841,7 +2916,8 @@ class _BinaryExpression(ColumnElement):
def self_group(self, against=None):
# use small/large defaults for comparison so that unknown
# operators are always parenthesized
- if self.operator is not against and operators.is_precedent(self.operator, against):
+ if self.operator is not against and \
+ operators.is_precedent(self.operator, against):
return _Grouping(self)
else:
return self
@@ -2870,7 +2946,8 @@ class _Exists(_UnaryExpression):
args = ([literal_column('*')],)
s = select(*args, **kwargs).as_scalar().self_group()
- _UnaryExpression.__init__(self, s, operator=operators.exists, type_=sqltypes.Boolean)
+ _UnaryExpression.__init__(self, s, operator=operators.exists,
+ type_=sqltypes.Boolean)
def select(self, whereclause=None, **params):
return select([self], whereclause, **params)
@@ -2881,8 +2958,8 @@ class _Exists(_UnaryExpression):
return e
def select_from(self, clause):
- """return a new exists() construct with the given expression set as its FROM
- clause.
+ """return a new exists() construct with the given expression set as
+ its FROM clause.
"""
e = self._clone()
@@ -2890,8 +2967,8 @@ class _Exists(_UnaryExpression):
return e
def where(self, clause):
- """return a new exists() construct with the given expression added to its WHERE
- clause, joined to the existing clause via AND, if any.
+ """return a new exists() construct with the given expression added to
+ its WHERE clause, joined to the existing clause via AND, if any.
"""
e = self._clone()
@@ -2899,7 +2976,8 @@ class _Exists(_UnaryExpression):
return e
class Join(FromClause):
- """represent a ``JOIN`` construct between two :class:`FromClause` elements.
+ """represent a ``JOIN`` construct between two :class:`FromClause`
+ elements.
The public constructor function for :class:`Join` is the module-level
:func:`join()` function, as well as the :func:`join()` method available
@@ -2937,7 +3015,8 @@ class Join(FromClause):
return _FromGrouping(self)
def _populate_column_collection(self):
- columns = [c for c in self.left.columns] + [c for c in self.right.columns]
+ columns = [c for c in self.left.columns] + \
+ [c for c in self.right.columns]
global sql_util
if not sql_util:
@@ -2945,7 +3024,8 @@ class Join(FromClause):
self._primary_key.extend(sql_util.reduce_columns(
(c for c in columns if c.primary_key), self.onclause))
self._columns.update((col._label, col) for col in columns)
- self._foreign_keys.update(itertools.chain(*[col.foreign_keys for col in columns]))
+ self._foreign_keys.update(itertools.chain(
+ *[col.foreign_keys for col in columns]))
def _copy_internals(self, clone=_clone):
self._reset_exported()
@@ -3000,7 +3080,8 @@ class Join(FromClause):
return self.left.bind or self.right.bind
def alias(self, name=None):
- """Create a :class:`Select` out of this :class:`Join` clause and return an :class:`Alias` of it.
+ """Create a :class:`Select` out of this :class:`Join` clause and
+ return an :class:`Alias` of it.
The :class:`Select` is not correlating.
@@ -3009,7 +3090,8 @@ class Join(FromClause):
@property
def _hide_froms(self):
- return itertools.chain(*[_from_objects(x.left, x.right) for x in self._cloned_set])
+ return itertools.chain(*[_from_objects(x.left, x.right)
+ for x in self._cloned_set])
@property
def _from_objects(self):
@@ -3046,7 +3128,8 @@ class Alias(FromClause):
if alias is None:
if self.original.named_with_column:
alias = getattr(self.original, 'name', None)
- alias = _generated_label('%%(%d %s)s' % (id(self), alias or 'anon'))
+ alias = _generated_label('%%(%d %s)s' % (id(self), alias
+ or 'anon'))
self.name = alias
@property
@@ -3061,7 +3144,8 @@ class Alias(FromClause):
try:
return self.element.as_scalar()
except AttributeError:
- raise AttributeError("Element %s does not support 'as_scalar()'" % self.element)
+ raise AttributeError("Element %s does not support "
+ "'as_scalar()'" % self.element)
def is_derived_from(self, fromclause):
if fromclause in self._cloned_set:
@@ -3080,7 +3164,8 @@ class Alias(FromClause):
baseselectable = baseselectable.element
self.original = baseselectable
- def get_children(self, column_collections=True, aliased_selectables=True, **kwargs):
+ def get_children(self, column_collections=True,
+ aliased_selectables=True, **kwargs):
if column_collections:
for c in self.c:
yield c
@@ -3180,10 +3265,9 @@ class _Label(ColumnElement):
def __init__(self, name, element, type_=None):
while isinstance(element, _Label):
element = element.element
- self.name = self.key = self._label = name or \
- _generated_label("%%(%d %s)s" % (
- id(self), getattr(element, 'name', 'anon'))
- )
+ self.name = self.key = self._label = name \
+ or _generated_label('%%(%d %s)s' % (id(self),
+ getattr(element, 'name', 'anon')))
self._element = element
self._type = type_
self.quote = element.quote
@@ -3314,8 +3398,10 @@ class ColumnClause(_Immutable, ColumnElement):
return []
def _bind_param(self, operator, obj):
- return _BindParamClause(self.name, obj, _compared_to_operator=operator,
- _compared_to_type=self.type, unique=True)
+ return _BindParamClause(self.name, obj,
+ _compared_to_operator=operator,
+ _compared_to_type=self.type,
+ unique=True)
def _make_proxy(self, selectable, name=None, attach=True):
# propagate the "is_literal" flag only if we are keeping our name,
@@ -3376,7 +3462,8 @@ class TableClause(_Immutable, FromClause):
return []
def count(self, whereclause=None, **params):
- """return a SELECT COUNT generated against this :class:`TableClause`."""
+ """return a SELECT COUNT generated against this
+ :class:`TableClause`."""
if self.primary_key:
col = list(self.primary_key)[0]
@@ -3423,9 +3510,12 @@ class _SelectBaseMixin(Executable):
self.use_labels = use_labels
self.for_update = for_update
if autocommit is not None:
- util.warn_deprecated("autocommit on select() is deprecated. "
- "Use .execution_options(autocommit=True)")
- self._execution_options = self._execution_options.union({'autocommit':autocommit})
+ util.warn_deprecated('autocommit on select() is '
+ 'deprecated. Use .execution_options(a'
+ 'utocommit=True)')
+ self._execution_options = \
+ self._execution_options.union({'autocommit'
+ : autocommit})
self._limit = limit
self._offset = offset
self._bind = bind
@@ -3469,16 +3559,21 @@ class _SelectBaseMixin(Executable):
return self.as_scalar().label(name)
@_generative
- @util.deprecated(message="autocommit() is deprecated. "
- "Use .execution_options(autocommit=True)")
+ @util.deprecated('0.6',
+ message=":func:`.autocommit` is deprecated. Use "
+ ":func:`.Executable.execution_options` with the "
+ "'autocommit' flag.")
def autocommit(self):
- """return a new selectable with the 'autocommit' flag set to True."""
-
- self._execution_options = self._execution_options.union({'autocommit':True})
+ """return a new selectable with the 'autocommit' flag set to
+ True."""
+
+ self._execution_options = \
+ self._execution_options.union({'autocommit': True})
def _generate(self):
- """Override the default _generate() method to also clear out exported collections."""
-
+ """Override the default _generate() method to also clear out
+ exported collections."""
+
s = self.__class__.__new__(self.__class__)
s.__dict__ = self.__dict__.copy()
s._reset_exported()
@@ -3486,32 +3581,40 @@ class _SelectBaseMixin(Executable):
@_generative
def limit(self, limit):
- """return a new selectable with the given LIMIT criterion applied."""
+ """return a new selectable with the given LIMIT criterion
+ applied."""
self._limit = limit
@_generative
def offset(self, offset):
- """return a new selectable with the given OFFSET criterion applied."""
+ """return a new selectable with the given OFFSET criterion
+ applied."""
self._offset = offset
@_generative
def order_by(self, *clauses):
- """return a new selectable with the given list of ORDER BY criterion applied.
-
- The criterion will be appended to any pre-existing ORDER BY criterion.
-
+ """return a new selectable with the given list of ORDER BY
+ criterion applied.
+
+ The criterion will be appended to any pre-existing ORDER BY
+ criterion.
+
"""
+
self.append_order_by(*clauses)
@_generative
def group_by(self, *clauses):
- """return a new selectable with the given list of GROUP BY criterion applied.
-
- The criterion will be appended to any pre-existing GROUP BY criterion.
-
+ """return a new selectable with the given list of GROUP BY
+ criterion applied.
+
+ The criterion will be appended to any pre-existing GROUP BY
+ criterion.
+
"""
+
self.append_group_by(*clauses)
def append_order_by(self, *clauses):
@@ -3555,9 +3658,10 @@ class _ScalarSelect(_Grouping):
@property
def columns(self):
- raise exc.InvalidRequestError("Scalar Select expression has no columns; "
- "use this object directly within a column-level expression.")
- c = columns
+ raise exc.InvalidRequestError('Scalar Select expression has no '
+ 'columns; use this object directly within a '
+ 'column-level expression.')
+ c = columns
def self_group(self, **kwargs):
return self
@@ -3592,12 +3696,11 @@ class CompoundSelect(_SelectBaseMixin, FromClause):
if not numcols:
numcols = len(s.c)
elif len(s.c) != numcols:
- raise exc.ArgumentError(
- "All selectables passed to CompoundSelect must "
- "have identical numbers of columns; select #%d has %d columns,"
- " select #%d has %d" %
- (1, len(self.selects[0].c), n+1, len(s.c))
- )
+ raise exc.ArgumentError('All selectables passed to '
+ 'CompoundSelect must have identical numbers of '
+ 'columns; select #%d has %d columns, select '
+ '#%d has %d' % (1, len(self.selects[0].c), n
+ + 1, len(s.c)))
self.selects.append(s.self_group(self))
@@ -3614,21 +3717,28 @@ class CompoundSelect(_SelectBaseMixin, FromClause):
def _populate_column_collection(self):
for cols in zip(*[s.c for s in self.selects]):
- # this is a slightly hacky thing - the union exports a column that
- # resembles just that of the *first* selectable. to get at a "composite" column,
- # particularly foreign keys, you have to dig through the proxies collection
- # which we generate below. We may want to improve upon this,
- # such as perhaps _make_proxy can accept a list of other columns that
- # are "shared" - schema.column can then copy all the ForeignKeys in.
- # this would allow the union() to have all those fks too.
- proxy = cols[0]._make_proxy(
- self, name=self.use_labels and cols[0]._label or None)
-
- # hand-construct the "proxies" collection to include all derived columns
- # place a 'weight' annotation corresponding to how low in the list of
- # select()s the column occurs, so that the corresponding_column() operation
- # can resolve conflicts
- proxy.proxies = [c._annotate({'weight':i + 1}) for i, c in enumerate(cols)]
+
+ # this is a slightly hacky thing - the union exports a
+ # column that resembles just that of the *first* selectable.
+ # to get at a "composite" column, particularly foreign keys,
+ # you have to dig through the proxies collection which we
+ # generate below. We may want to improve upon this, such as
+ # perhaps _make_proxy can accept a list of other columns
+ # that are "shared" - schema.column can then copy all the
+ # ForeignKeys in. this would allow the union() to have all
+ # those fks too.
+
+ proxy = cols[0]._make_proxy(self, name=self.use_labels
+ and cols[0]._label or None)
+
+ # hand-construct the "proxies" collection to include all
+ # derived columns place a 'weight' annotation corresponding
+ # to how low in the list of select()s the column occurs, so
+ # that the corresponding_column() operation can resolve
+ # conflicts
+
+ proxy.proxies = [c._annotate({'weight': i + 1}) for (i,
+ c) in enumerate(cols)]
def _copy_internals(self, clone=_clone):
self._reset_exported()
@@ -3640,8 +3750,9 @@ class CompoundSelect(_SelectBaseMixin, FromClause):
setattr(self, attr, clone(getattr(self, attr)))
def get_children(self, column_collections=True, **kwargs):
- return (column_collections and list(self.c) or []) + \
- [self._order_by_clause, self._group_by_clause] + list(self.selects)
+ return (column_collections and list(self.c) or []) \
+ + [self._order_by_clause, self._group_by_clause] \
+ + list(self.selects)
def bind(self):
if self._bind:
@@ -3752,16 +3863,18 @@ class Select(_SelectBaseMixin, FromClause):
if len(froms) > 1 or self._correlate:
if self._correlate:
- froms = froms.difference(_cloned_intersection(froms, self._correlate))
-
+ froms = froms.difference(_cloned_intersection(froms,
+ self._correlate))
if self._should_correlate and existing_froms:
- froms = froms.difference(_cloned_intersection(froms, existing_froms))
+ froms = froms.difference(_cloned_intersection(froms,
+ existing_froms))
if not len(froms):
- raise exc.InvalidRequestError(
- "Select statement '%s' returned no FROM clauses "
- "due to auto-correlation; specify correlate(<tables>) "
- "to control correlation manually." % self)
+ raise exc.InvalidRequestError("Select statement '%s"
+ "' returned no FROM clauses due to "
+ "auto-correlation; specify "
+ "correlate(<tables>) to control "
+ "correlation manually." % self)
return froms
@@ -3773,21 +3886,23 @@ class Select(_SelectBaseMixin, FromClause):
@_generative
def with_hint(self, selectable, text, dialect_name=None):
- """Add an indexing hint for the given selectable to this :class:`Select`.
+ """Add an indexing hint for the given selectable to this
+ :class:`Select`.
The text of the hint is written specific to a specific backend, and
typically uses Python string substitution syntax to render the name
of the table or alias, such as for Oracle::
- select([mytable]).with_hint(mytable, "+ index(%(name)s ix_mytable)")
+ select([mytable]).with_hint(mytable, "+ index(%(name)s
+ ix_mytable)")
Would render SQL as::
select /*+ index(mytable ix_mytable) */ ... from mytable
- The ``dialect_name`` option will limit the rendering of a particular hint
- to a particular backend. Such as, to add hints for both Oracle and
- Sybase simultaneously::
+ The ``dialect_name`` option will limit the rendering of a particular
+ hint to a particular backend. Such as, to add hints for both Oracle
+ and Sybase simultaneously::
select([mytable]).\
with_hint(mytable, "+ index(%(name)s ix_mytable)", 'oracle').\
@@ -3807,9 +3922,10 @@ class Select(_SelectBaseMixin, FromClause):
@util.memoized_instancemethod
def locate_all_froms(self):
"""return a Set of all FromClause elements referenced by this Select.
-
- This set is a superset of that returned by the ``froms`` property, which
- is specifically for those FromClause elements that would actually be rendered.
+
+ This set is a superset of that returned by the ``froms`` property,
+ which is specifically for those FromClause elements that would
+ actually be rendered.
"""
return self._froms.union(_from_objects(*list(self._froms)))
@@ -3838,7 +3954,8 @@ class Select(_SelectBaseMixin, FromClause):
self._froms = util.OrderedSet(from_cloned[f] for f in self._froms)
self._correlate = set(from_cloned[f] for f in self._correlate)
self._raw_columns = [clone(c) for c in self._raw_columns]
- for attr in ('_whereclause', '_having', '_order_by_clause', '_group_by_clause'):
+ for attr in '_whereclause', '_having', '_order_by_clause', \
+ '_group_by_clause':
if getattr(self, attr) is not None:
setattr(self, attr, clone(getattr(self, attr)))
@@ -3882,8 +3999,8 @@ class Select(_SelectBaseMixin, FromClause):
@_generative
def where(self, whereclause):
- """return a new select() construct with the given expression added to its
- WHERE clause, joined to the existing clause via AND, if any.
+ """return a new select() construct with the given expression added to
+ its WHERE clause, joined to the existing clause via AND, if any.
"""
@@ -3891,24 +4008,24 @@ class Select(_SelectBaseMixin, FromClause):
@_generative
def having(self, having):
- """return a new select() construct with the given expression added to its HAVING
- clause, joined to the existing clause via AND, if any.
+ """return a new select() construct with the given expression added to
+ its HAVING clause, joined to the existing clause via AND, if any.
"""
self.append_having(having)
@_generative
def distinct(self):
- """return a new select() construct which will apply DISTINCT to its columns
- clause.
+ """return a new select() construct which will apply DISTINCT to its
+ columns clause.
"""
self._distinct = True
@_generative
def prefix_with(self, clause):
- """return a new select() construct which will apply the given expression to the
- start of its columns clause, not using any commas.
+ """return a new select() construct which will apply the given
+ expression to the start of its columns clause, not using any commas.
"""
clause = _literal_as_text(clause)
@@ -3916,8 +4033,8 @@ class Select(_SelectBaseMixin, FromClause):
@_generative
def select_from(self, fromclause):
- """return a new select() construct with the given FROM expression applied to its
- list of FROM objects.
+ """return a new select() construct with the given FROM expression
+ applied to its list of FROM objects.
"""
fromclause = _literal_as_text(fromclause)
@@ -3925,17 +4042,19 @@ class Select(_SelectBaseMixin, FromClause):
@_generative
def correlate(self, *fromclauses):
- """return a new select() construct which will correlate the given FROM clauses to
- that of an enclosing select(), if a match is found.
+ """return a new select() construct which will correlate the given FROM
+ clauses to that of an enclosing select(), if a match is found.
- By "match", the given fromclause must be present in this select's list of FROM
- objects and also present in an enclosing select's list of FROM objects.
+ By "match", the given fromclause must be present in this select's
+ list of FROM objects and also present in an enclosing select's list of
+ FROM objects.
- Calling this method turns off the select's default behavior of
- "auto-correlation". Normally, select() auto-correlates all of its FROM clauses to
- those of an embedded select when compiled.
+ Calling this method turns off the select's default behavior of
+ "auto-correlation". Normally, select() auto-correlates all of its FROM
+ clauses to those of an embedded select when compiled.
- If the fromclause is None, correlation is disabled for the returned select().
+ If the fromclause is None, correlation is disabled for the returned
+ select().
"""
self._should_correlate = False
@@ -3945,14 +4064,15 @@ class Select(_SelectBaseMixin, FromClause):
self._correlate = self._correlate.union(fromclauses)
def append_correlation(self, fromclause):
- """append the given correlation expression to this select() construct."""
+ """append the given correlation expression to this select()
+ construct."""
self._should_correlate = False
self._correlate = self._correlate.union([fromclause])
def append_column(self, column):
- """append the given column expression to the columns clause of this select()
- construct.
+ """append the given column expression to the columns clause of this
+ select() construct.
"""
column = _literal_as_column(column)
@@ -3973,7 +4093,8 @@ class Select(_SelectBaseMixin, FromClause):
self._prefixes = self._prefixes + (clause,)
def append_whereclause(self, whereclause):
- """append the given expression to this select() construct's WHERE criterion.
+ """append the given expression to this select() construct's WHERE
+ criterion.
The expression will be joined to existing WHERE criterion via AND.
@@ -3987,7 +4108,8 @@ class Select(_SelectBaseMixin, FromClause):
self._whereclause = whereclause
def append_having(self, having):
- """append the given expression to this select() construct's HAVING criterion.
+ """append the given expression to this select() construct's HAVING
+ criterion.
The expression will be joined to existing HAVING criterion via AND.
@@ -3998,8 +4120,8 @@ class Select(_SelectBaseMixin, FromClause):
self._having = _literal_as_text(having)
def append_from(self, fromclause):
- """append the given FromClause expression to this select() construct's FROM
- clause.
+ """append the given FromClause expression to this select() construct's
+ FROM clause.
"""
if _is_literal(fromclause):
@@ -4022,10 +4144,12 @@ class Select(_SelectBaseMixin, FromClause):
c._make_proxy(self, name=self.use_labels and c._label or None)
def self_group(self, against=None):
- """return a 'grouping' construct as per the ClauseElement specification.
-
- This produces an element that can be embedded in an expression. Note that
- this method is called automatically as needed when constructing expressions.
+ """return a 'grouping' construct as per the ClauseElement
+ specification.
+
+ This produces an element that can be embedded in an expression. Note
+ that this method is called automatically as needed when constructing
+ expressions.
"""
if isinstance(against, CompoundSelect):
@@ -4033,7 +4157,8 @@ class Select(_SelectBaseMixin, FromClause):
return _FromGrouping(self)
def union(self, other, **kwargs):
- """return a SQL UNION of this select() construct against the given selectable."""
+ """return a SQL UNION of this select() construct against the given
+ selectable."""
return union(self, other, **kwargs)
@@ -4045,13 +4170,14 @@ class Select(_SelectBaseMixin, FromClause):
return union_all(self, other, **kwargs)
def except_(self, other, **kwargs):
- """return a SQL EXCEPT of this select() construct against the given selectable."""
+ """return a SQL EXCEPT of this select() construct against the given
+ selectable."""
return except_(self, other, **kwargs)
def except_all(self, other, **kwargs):
- """return a SQL EXCEPT ALL of this select() construct against the given
- selectable.
+ """return a SQL EXCEPT ALL of this select() construct against the
+ given selectable.
"""
return except_all(self, other, **kwargs)
@@ -4064,8 +4190,8 @@ class Select(_SelectBaseMixin, FromClause):
return intersect(self, other, **kwargs)
def intersect_all(self, other, **kwargs):
- """return a SQL INTERSECT ALL of this select() construct against the given
- selectable.
+ """return a SQL INTERSECT ALL of this select() construct against the
+ given selectable.
"""
return intersect_all(self, other, **kwargs)
@@ -4096,7 +4222,8 @@ class _UpdateBase(Executable, ClauseElement):
__visit_name__ = 'update_base'
- _execution_options = Executable._execution_options.union({'autocommit':True})
+ _execution_options = \
+ Executable._execution_options.union({'autocommit': True})
kwargs = util.frozendict()
def _process_colparams(self, parameters):
@@ -4137,10 +4264,10 @@ class _UpdateBase(Executable, ClauseElement):
def returning(self, *cols):
"""Add a RETURNING or equivalent clause to this statement.
- The given list of columns represent columns within the table
- that is the target of the INSERT, UPDATE, or DELETE. Each
- element can be any column expression. :class:`~sqlalchemy.schema.Table`
- objects will be expanded into their individual columns.
+ The given list of columns represent columns within the table that is
+ the target of the INSERT, UPDATE, or DELETE. Each element can be any
+ column expression. :class:`~sqlalchemy.schema.Table` objects will be
+ expanded into their individual columns.
Upon compilation, a RETURNING clause, or database equivalent,
will be rendered within the statement. For INSERT and UPDATE,
@@ -4177,15 +4304,16 @@ class _ValuesBase(_UpdateBase):
@_generative
def values(self, *args, **kwargs):
- """specify the VALUES clause for an INSERT statement, or the SET clause for an
- UPDATE.
+ """specify the VALUES clause for an INSERT statement, or the SET
+ clause for an UPDATE.
\**kwargs
key=<somevalue> arguments
\*args
- A single dictionary can be sent as the first positional argument. This
- allows non-string based keys, such as Column objects, to be used.
+ A single dictionary can be sent as the first positional
+ argument. This allows non-string based keys, such as Column
+ objects, to be used.
"""
if args:
@@ -4292,12 +4420,13 @@ class Update(_ValuesBase):
@_generative
def where(self, whereclause):
- """return a new update() construct with the given expression added to its WHERE
- clause, joined to the existing clause via AND, if any.
+ """return a new update() construct with the given expression added to
+ its WHERE clause, joined to the existing clause via AND, if any.
"""
if self._whereclause is not None:
- self._whereclause = and_(self._whereclause, _literal_as_text(whereclause))
+ self._whereclause = and_(self._whereclause,
+ _literal_as_text(whereclause))
else:
self._whereclause = _literal_as_text(whereclause)
@@ -4340,7 +4469,8 @@ class Delete(_UpdateBase):
"""Add the given WHERE clause to a newly returned delete construct."""
if self._whereclause is not None:
- self._whereclause = and_(self._whereclause, _literal_as_text(whereclause))
+ self._whereclause = and_(self._whereclause,
+ _literal_as_text(whereclause))
else:
self._whereclause = _literal_as_text(whereclause)
@@ -4349,8 +4479,10 @@ class Delete(_UpdateBase):
self._whereclause = clone(self._whereclause)
class _IdentifiedClause(Executable, ClauseElement):
+
__visit_name__ = 'identified'
- _execution_options = Executable._execution_options.union({'autocommit':False})
+ _execution_options = \
+ Executable._execution_options.union({'autocommit': False})
quote = None
def __init__(self, ident):
diff --git a/lib/sqlalchemy/test/testing.py b/lib/sqlalchemy/test/testing.py
index 70ddc7ba2..78cd74d22 100644
--- a/lib/sqlalchemy/test/testing.py
+++ b/lib/sqlalchemy/test/testing.py
@@ -533,6 +533,7 @@ def assert_raises_message(except_cls, msg, callable_, *args, **kwargs):
assert False, "Callable did not raise an exception"
except except_cls, e:
assert re.search(msg, str(e)), "%r !~ %s" % (msg, e)
+ print str(e)
def fail(msg):
assert False, msg
diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py
index 91eb35275..777353714 100644
--- a/lib/sqlalchemy/types.py
+++ b/lib/sqlalchemy/types.py
@@ -1024,20 +1024,25 @@ class Numeric(_DateAffinity, TypeEngine):
operators.mul:{
Interval:Interval,
Numeric:Numeric,
+ Integer:Numeric,
},
# Py2K
operators.div:{
Numeric:Numeric,
+ Integer:Numeric,
},
# end Py2K
operators.truediv:{
Numeric:Numeric,
+ Integer:Numeric,
},
operators.add:{
Numeric:Numeric,
+ Integer:Numeric,
},
operators.sub:{
Numeric:Numeric,
+ Integer:Numeric,
}
}
diff --git a/lib/sqlalchemy/util.py b/lib/sqlalchemy/util.py
index c26f38b6b..d13a2685c 100644
--- a/lib/sqlalchemy/util.py
+++ b/lib/sqlalchemy/util.py
@@ -4,7 +4,14 @@
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
-import inspect, itertools, operator, sys, warnings, weakref, gc
+import inspect
+import itertools
+import operator
+import sys
+import warnings
+import weakref
+import re
+
# Py2K
import __builtin__
# end Py2K
@@ -1625,21 +1632,23 @@ def warn_deprecated(msg, stacklevel=3):
def warn_pending_deprecation(msg, stacklevel=3):
warnings.warn(msg, exc.SAPendingDeprecationWarning, stacklevel=stacklevel)
-def deprecated(message=None, add_deprecation_to_docstring=True):
+def deprecated(version, message=None, add_deprecation_to_docstring=True):
"""Decorates a function and issues a deprecation warning on use.
- message
+ :param message:
If provided, issue message in the warning. A sensible default
is used if not provided.
- add_deprecation_to_docstring
+ :param add_deprecation_to_docstring:
Default True. If False, the wrapped function's __doc__ is left
as-is. If True, the 'message' is prepended to the docs if
provided, or sensible default if message is omitted.
+
"""
if add_deprecation_to_docstring:
- header = message is not None and message or 'Deprecated.'
+ header = ".. deprecated:: %s %s" % \
+ (version, (message or ''))
else:
header = None
@@ -1656,37 +1665,49 @@ def pending_deprecation(version, message=None,
add_deprecation_to_docstring=True):
"""Decorates a function and issues a pending deprecation warning on use.
- version
+ :param version:
An approximate future version at which point the pending deprecation
will become deprecated. Not used in messaging.
- message
+ :param message:
If provided, issue message in the warning. A sensible default
is used if not provided.
- add_deprecation_to_docstring
+ :param add_deprecation_to_docstring:
Default True. If False, the wrapped function's __doc__ is left
as-is. If True, the 'message' is prepended to the docs if
provided, or sensible default if message is omitted.
"""
if add_deprecation_to_docstring:
- header = ".. deprecated:: %s (pending) %s" % (version, (message or ''))
+ header = ".. deprecated:: %s (pending) %s" % \
+ (version, (message or ''))
else:
header = None
if message is None:
message = "Call to deprecated function %(func)s"
-
+
def decorate(fn):
return _decorate_with_warning(
fn, exc.SAPendingDeprecationWarning,
message % dict(func=fn.__name__), header)
return decorate
+def _sanitize_rest(text):
+ def repl(m):
+ type_, name = m.group(1, 2)
+ if type_ in ("func", "meth"):
+ name += "()"
+ return name
+ return re.sub(r'\:(\w+)\:`~?\.?(.+?)`', repl, text)
+
+
def _decorate_with_warning(func, wtype, message, docstring_header=None):
"""Wrap a function with a warnings.warn and augmented docstring."""
+ message = _sanitize_rest(message)
+
@decorator
def warned(fn, *args, **kwargs):
warnings.warn(wtype(message), stacklevel=3)