]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Warnings are now issued as SAWarning instead of RuntimeWarning; util.warn() wraps...
authorJason Kirtland <jek@discorporate.us>
Fri, 11 Jan 2008 01:28:43 +0000 (01:28 +0000)
committerJason Kirtland <jek@discorporate.us>
Fri, 11 Jan 2008 01:28:43 +0000 (01:28 +0000)
- SADeprecationWarning has moved to exceptions. An alias remains in logging until 0.5.

18 files changed:
CHANGES
lib/sqlalchemy/databases/firebird.py
lib/sqlalchemy/databases/informix.py
lib/sqlalchemy/databases/maxdb.py
lib/sqlalchemy/databases/mssql.py
lib/sqlalchemy/databases/mysql.py
lib/sqlalchemy/databases/oracle.py
lib/sqlalchemy/databases/postgres.py
lib/sqlalchemy/databases/sqlite.py
lib/sqlalchemy/databases/sybase.py
lib/sqlalchemy/exceptions.py
lib/sqlalchemy/logging.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/sql/expression.py
lib/sqlalchemy/types.py
lib/sqlalchemy/util.py

diff --git a/CHANGES b/CHANGES
index 32970990986764b911c178ea61882429e8ea2976..ab87481c54cf9a7b306e98bdc1568ebb98cd3a75 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -5,27 +5,28 @@ CHANGES
 0.4.2p4
 ------
 - orm
-    - proper error message is raised when trying to 
-      access expired instance attributes with no session
-      present
-    
+    - proper error message is raised when trying to access
+      expired instance attributes with no session present
+
     - added a mapper() flag "eager_defaults"; when set to
-      True, defaults that are generated during an INSERT
-      or UPDATE operation are post-fetched immediately, 
-      instead of being deferred until later.  This mimics
-      the old 0.3 behavior.
-      
+      True, defaults that are generated during an INSERT or
+      UPDATE operation are post-fetched immediately, instead
+      of being deferred until later.  This mimics the old 0.3
+      behavior.
+
+- general
+    - warnings are now issued as type exceptions.SAWarning.
+
 - dialects
-    - finally added PGMacAddr type to postgres 
-      [ticket:580]
-    
+    - finally added PGMacAddr type to postgres [ticket:580]
+
 0.4.2p3
 ------
 - general
     - sub version numbering scheme changed to suite
       setuptools version number rules; easy_install -u
       should now get this version over 0.4.2.
-      
+
 - sql
     - Text type is properly exported now and does not
       raise a warning on DDL create; String types with no
@@ -34,7 +35,7 @@ CHANGES
 
     - new UnicodeText type is added, to specify an
       encoded, unlengthed Text type
-      
+
     - fixed bug in union() so that select() statements
       which don't derive from FromClause objects can be
       unioned
@@ -42,11 +43,11 @@ CHANGES
 - orm
     - fixed bug with session.dirty when using "mutable
       scalars" (such as PickleTypes)
-      
+
     - added a more descriptive error message when flushing
       on a relation() that has non-locally-mapped columns
       in its primary or secondary join condition
-      
+
 - dialects
     - Fixed reflection of mysql empty string column
       defaults.
index e16593eb87ec692059ebb36859b9a31d5d0cd482..c83a44e016c5e7dd8e605e6818d48ae3ff90ff61 100644 (file)
@@ -88,7 +88,6 @@ connections are active, the following setting may alleviate the problem::
 
 
 import datetime
-import warnings
 
 from sqlalchemy import exceptions, schema, types as sqltypes, sql, util
 from sqlalchemy.engine import base, default
@@ -461,7 +460,8 @@ class FBDialect(default.DefaultDialect):
             # get the data types and lengths
             coltype = ischema_names.get(row['ftype'].rstrip())
             if coltype is None:
-                warnings.warn(RuntimeWarning("Did not recognize type '%s' of column '%s'" % (str(row['ftype']), name)))
+                util.warn("Did not recognize type '%s' of column '%s'" %
+                          (str(row['ftype']), name))
                 coltype = sqltypes.NULLTYPE
             else:
                 coltype = coltype(row)
index eb07932433b464e89d054db14214a223d44ee820..03fbcd67e46e258938ca60f87d9612c57fd7ed2d 100644 (file)
@@ -6,7 +6,7 @@
 # This module is part of SQLAlchemy and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
-import datetime, warnings
+import datetime
 
 from sqlalchemy import sql, schema, exceptions, pool
 from sqlalchemy.sql import compiler
@@ -311,7 +311,8 @@ class InfoDialect(default.DefaultDialect):
                 try:
                     coltype = ischema_names[coltype]
                 except KeyError:
-                    warnings.warn(RuntimeWarning("Did not recognize type '%s' of column '%s'" % (coltype, name)))
+                    util.warn("Did not recognize type '%s' of column '%s'" %
+                              (coltype, name))
                     coltype = sqltypes.NULLTYPE
 
             colargs = []
index 4aa59bc28c5aacad00e8aaae3211c8fdb64330e1..23ff1f4a000f4d3d89c5629f1e9846cb35ea04b0 100644 (file)
@@ -56,7 +56,7 @@ integration- email the devel list if you're interested in working on
 this.
 """
 
-import datetime, itertools, re, warnings
+import datetime, itertools, re
 
 from sqlalchemy import exceptions, schema, sql, util
 from sqlalchemy.sql import operators as sql_operators, expression as sql_expr
@@ -632,9 +632,8 @@ class MaxDBDialect(default.DefaultDialect):
                 type_cls = ischema_names[col_type.lower()]
                 type_instance = type_cls(*type_args, **type_kw)
             except KeyError:
-                warnings.warn(RuntimeWarning(
-                    "Did not recognize type '%s' of column '%s'" %
-                    (col_type, name)))
+                util.warn("Did not recognize type '%s' of column '%s'" %
+                          (col_type, name))
                 type_instance = sqltypes.NullType
 
             col_kw = {'autoincrement': False}
index 572139d489017758fafc84f8b62e9954646353ab..2ec645eeaf1a441b6495313c01de24cf670b65dc 100644 (file)
@@ -37,7 +37,7 @@ Known issues / TODO:
 
 """
 
-import datetime, random, warnings, re, sys, operator
+import datetime, operator, random, re, sys
 
 from sqlalchemy import sql, schema, exceptions, util
 from sqlalchemy.sql import compiler, expression, operators as sqlops
@@ -585,7 +585,8 @@ class MSSQLDialect(default.DefaultDialect):
                 coltype = MSText()
             else:
                 if coltype is None:
-                    warnings.warn(RuntimeWarning("Did not recognize type '%s' of column '%s'" % (type, name)))
+                    util.warn("Did not recognize type '%s' of column '%s'" %
+                              (type, name))
                     coltype = sqltypes.NULLTYPE
 
                 elif coltype in (MSNVarchar, AdoMSNVarchar) and charlen == -1:
index 17defbb70c8be9655616d554cb475a4d838470e1..e7e3ec069f201b7d2f5d0c73d561dcc92fedb4e5 100644 (file)
@@ -143,7 +143,7 @@ Notes page on the wiki at http://www.sqlalchemy.org is a good resource for
 timely information affecting MySQL in SQLAlchemy.
 """
 
-import re, datetime, inspect, warnings, sys
+import datetime, inspect, re, sys
 from array import array as _array
 
 from sqlalchemy import exceptions, logging, schema, sql, util
@@ -1695,10 +1695,10 @@ class MySQLDialect(default.DefaultDialect):
             if 'character_set' in opts:
                 return opts['character_set']
             else:
-                warnings.warn(RuntimeWarning(
+                util.warn(
                     "Could not detect the connection character set with this "
                     "combination of MySQL server and MySQL-python. "
-                    "MySQL-python >= 1.2.2 is recommended.  Assuming latin1."))
+                    "MySQL-python >= 1.2.2 is recommended.  Assuming latin1.")
                 return 'latin1'
 
     def _detect_casing(self, connection, charset=None):
@@ -2068,9 +2068,7 @@ class MySQLSchemaReflector(object):
             else:
                 type_, spec = self.parse_constraints(line)
                 if type_ is None:
-                    warnings.warn(
-                        RuntimeWarning("Unknown schema content: %s" %
-                                       repr(line)))
+                    util.warn("Unknown schema content: %r" % line)
                 elif type_ == 'key':
                     keys.append(spec)
                 elif type_ == 'constraint':
@@ -2098,12 +2096,10 @@ class MySQLSchemaReflector(object):
     def _add_column(self, table, line, charset, only=None):
         spec = self.parse_column(line)
         if not spec:
-            warnings.warn(RuntimeWarning(
-                "Unknown column definition %s" % line))
+            util.warn("Unknown column definition %r" % line)
             return
         if not spec['full']:
-            warnings.warn(RuntimeWarning(
-                "Incomplete reflection of column definition %s" % line))
+            util.warn("Incomplete reflection of column definition %r" % line)
 
         name, type_, args, notnull = \
               spec['name'], spec['coltype'], spec['arg'], spec['notnull']
@@ -2121,9 +2117,8 @@ class MySQLSchemaReflector(object):
         try:
             col_type = ischema_names[type_]
         except KeyError:
-            warnings.warn(RuntimeWarning(
-                "Did not recognize type '%s' of column '%s'" %
-                (type_, name)))
+            util.warn("Did not recognize type '%s' of column '%s'" %
+                      (type_, name))
             col_type = sqltypes.NullType
 
         # Column type positional arguments eg. varchar(32)
index 394aba178cb15e54bd9e9bfb5c6a188720e863ba..c7ab134417496598fd2bb5ea738ffb38b1e6964e 100644 (file)
@@ -5,7 +5,7 @@
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
 
-import re, warnings, random
+import datetime, random, re
 
 from sqlalchemy import util, sql, schema, exceptions, logging
 from sqlalchemy.engine import default, base
@@ -13,8 +13,6 @@ from sqlalchemy.sql import compiler, visitors
 from sqlalchemy.sql import operators as sql_operators
 from sqlalchemy import types as sqltypes
 
-import datetime
-
 
 class OracleNumeric(sqltypes.Numeric):
     def get_col_spec(self):
@@ -501,7 +499,8 @@ class OracleDialect(default.DefaultDialect):
                 try:
                     coltype = ischema_names[coltype]
                 except KeyError:
-                    warnings.warn(RuntimeWarning("Did not recognize type '%s' of column '%s'" % (coltype, colname)))
+                    util.warn("Did not recognize type '%s' of column '%s'" %
+                              (coltype, colname))
                     coltype = sqltypes.NULLTYPE
 
             colargs = []
@@ -551,7 +550,10 @@ class OracleDialect(default.DefaultDialect):
                    fks[cons_name] = fk
                 if remote_table is None:
                     # ticket 363
-                    warnings.warn("Got 'None' querying 'table_name' from all_cons_columns%(dblink)s - does the user have proper rights to the table?" % {'dblink':dblink})
+                    util.warn(
+                        ("Got 'None' querying 'table_name' from "
+                         "all_cons_columns%(dblink)s - does the user have "
+                         "proper rights to the table?") % {'dblink':dblink})
                     continue
                 refspec = ".".join([remote_table, remote_column])
                 schema.Table(remote_table, table.metadata, autoload=True, autoload_with=connection, owner=remote_owner)
index 62372698026b737fe306a4ce4ed5a4b4738ff396..19db8b6b792388a03e1b692bae5bab66cdd62a5d 100644 (file)
@@ -19,7 +19,7 @@ parameter when creating the queries::
     postgres_returning=[empl.c.id, empl.c.salary]).execute().fetchall()
 """
 
-import re, random, warnings, string
+import random, re, string
 
 from sqlalchemy import sql, schema, exceptions, util
 from sqlalchemy.engine import base, default
@@ -511,7 +511,8 @@ class PGDialect(default.DefaultDialect):
                 if is_array:
                     coltype = PGArray(coltype)
             else:
-                warnings.warn(RuntimeWarning("Did not recognize type '%s' of column '%s'" % (attype, name)))
+                util.warn("Did not recognize type '%s' of column '%s'" %
+                          (attype, name))
                 coltype = sqltypes.NULLTYPE
 
             colargs= []
index 36e05a067a15df3db081f6d9b3457e76b8fd9544..4ce8987272159909c20fa61ce45a7cf32243b121 100644 (file)
@@ -5,12 +5,11 @@
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
 
-import re
+import datetime, re, time
 
 from sqlalchemy import schema, exceptions, pool, PassiveDefault
 from sqlalchemy.engine import default
 import sqlalchemy.types as sqltypes
-import datetime,time, warnings
 import sqlalchemy.util as util
 from sqlalchemy.sql import compiler
 
@@ -202,7 +201,11 @@ class SQLiteDialect(default.DefaultDialect):
         if self.dbapi is not None:
             sqlite_ver = self.dbapi.version_info
             if sqlite_ver < (2,1,'3'):
-                warnings.warn(RuntimeWarning("The installed version of pysqlite2 (%s) is out-dated, and will cause errors in some cases.  Version 2.1.3 or greater is recommended." % '.'.join([str(subver) for subver in sqlite_ver])))
+                util.warn(
+                    ("The installed version of pysqlite2 (%s) is out-dated "
+                     "and will cause errors in some cases.  Version 2.1.3 "
+                     "or greater is recommended.") %
+                    '.'.join([str(subver) for subver in sqlite_ver]))
         self.supports_cast = (self.dbapi is None or vers(self.dbapi.sqlite_version) >= vers("3.2.3"))
 
     def dbapi(cls):
@@ -281,7 +284,8 @@ class SQLiteDialect(default.DefaultDialect):
             try:
                 coltype = ischema_names[coltype]
             except KeyError:
-                warnings.warn(RuntimeWarning("Did not recognize type '%s' of column '%s'" % (coltype, name)))
+                util.warn("Did not recognize type '%s' of column '%s'" %
+                          (coltype, name))
                 coltype = sqltypes.NullType
 
             if args is not None:
index f7c3d8a0f00a7cc6de942b4f745814728a5aa5b1..bf2b6b7d6a5608447fe45bf242fd18d05f525818 100644 (file)
@@ -22,7 +22,7 @@ Known issues / TODO:
  * Tested on 'Adaptive Server Anywhere 9' (version 9.0.1.1751)
 """
 
-import datetime, random, warnings, operator
+import datetime, operator, random
 
 from sqlalchemy import util, sql, schema, exceptions
 from sqlalchemy.sql import compiler, expression
@@ -593,7 +593,8 @@ class SybaseSQLDialect(default.DefaultDialect):
                 coltype = SybaseText()
             else:
                 if coltype is None:
-                    warnings.warn(RuntimeWarning("Did not recognize type '%s' of column '%s'" % (type, name)))
+                    util.warn("Did not recognize type '%s' of column '%s'" %
+                              (type, name))
                     coltype = sqltypes.NULLTYPE
                 coltype = coltype(*args)
             colargs= []
index 02cee506322ce682f98097edb7e373fab34be614..eda368d7cc396dc2a0e3a13dcfeb4f4e269c669a 100644 (file)
@@ -21,7 +21,7 @@ class ArgumentError(SQLAlchemyError):
 
 class CompileError(SQLAlchemyError):
     """Raised when an error occurs during SQL compilation"""
-        
+
 
 class TimeoutError(SQLAlchemyError):
     """Raised when a connection pool times out on getting a connection."""
@@ -33,7 +33,7 @@ class ConcurrentModificationError(SQLAlchemyError):
 
 class CircularDependencyError(SQLAlchemyError):
     """Raised by topological sorts when a circular dependency is detected"""
-    
+
 
 class FlushError(SQLAlchemyError):
     """Raised when an invalid condition is detected upon a ``flush()``."""
@@ -47,9 +47,9 @@ class InvalidRequestError(SQLAlchemyError):
     """
 
 class UnmappedColumnError(InvalidRequestError):
-    """A mapper was asked to return mapped information about a column 
+    """A mapper was asked to return mapped information about a column
     which it does not map"""
-    
+
 class NoSuchTableError(InvalidRequestError):
     """SQLAlchemy was asked to load a table's definition from the
     database, but the table doesn't exist.
@@ -66,7 +66,7 @@ class NoSuchColumnError(KeyError, SQLAlchemyError):
 
 class DisconnectionError(SQLAlchemyError):
     """Raised within ``Pool`` when a disconnect is detected on a raw DB-API connection.
-    
+
     This error is consumed internally by a connection pool.  It can be raised by
     a ``PoolListener`` so that the host pool forces a disconnect.
     """
@@ -88,7 +88,7 @@ class DBAPIError(SQLAlchemyError):
     the exception object in the ``statement`` and ``params`` attributes.
 
     The wrapped exception object is available in the ``orig`` attribute.
-    Its type and properties are DB-API implementation specific.  
+    Its type and properties are DB-API implementation specific.
     """
 
     def instance(cls, statement, params, orig, connection_invalidated=False):
@@ -96,15 +96,15 @@ class DBAPIError(SQLAlchemyError):
         # DBAPIError didn't exist.
         if isinstance(orig, (KeyboardInterrupt, SystemExit)):
             return orig
-        
+
         if orig is not None:
             name, glob = orig.__class__.__name__, globals()
             if name in glob and issubclass(glob[name], DBAPIError):
                 cls = glob[name]
-            
+
         return cls(statement, params, orig, connection_invalidated)
     instance = classmethod(instance)
-    
+
     def __init__(self, statement, params, orig, connection_invalidated=False):
         try:
             text = str(orig)
@@ -150,3 +150,10 @@ class ProgrammingError(DatabaseError):
 
 class NotSupportedError(DatabaseError):
     """Wraps a DB-API NotSupportedError."""
+
+# Warnings
+class SADeprecationWarning(DeprecationWarning):
+    """Issued once per usage of a deprecated API."""
+
+class SAWarning(RuntimeWarning):
+    """Issued at runtime."""
index 57e7bcfd7da59517b3cd374063ccf3ea9d5bb9ab..4711b315577015dd278d77767e7f36831ff75ac9 100644 (file)
@@ -4,17 +4,18 @@
 # This module is part of SQLAlchemy and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
-"""Provides a few functions used by instances to turn on/off their
-logging, including support for the usual "echo" parameter.
+"""Logging control and utilities.
 
-Control of logging for SA can be performed from the regular python
-logging module.  The regular dotted module namespace is used, starting
-at 'sqlalchemy'.  For class-level logging, the class name is appended,
-and for instance-level logging, the hex id of the instance is
-appended.
+Provides a few functions used by instances to turn on/off their logging,
+including support for the usual "echo" parameter.
 
-The "echo" keyword parameter which is available on some SA objects
-corresponds to an instance-level logger for that instance.
+Control of logging for SA can be performed from the regular python logging
+module.  The regular dotted module namespace is used, starting at
+'sqlalchemy'.  For class-level logging, the class name is appended, and for
+instance-level logging, the hex id of the instance is appended.
+
+The "echo" keyword parameter which is available on some SA objects corresponds
+to an instance-level logger for that instance.
 
 E.g.::
 
@@ -23,21 +24,22 @@ E.g.::
 is equivalent to::
 
     import logging
-    logging.getLogger('sqlalchemy.engine.Engine.%s' % hex(id(engine))).setLevel(logging.DEBUG)
+    logger = logging.getLogger('sqlalchemy.engine.Engine.%s' % hex(id(engine)))
+    logger.setLevel(logging.DEBUG)
 """
 
 import sys, warnings
+import sqlalchemy.exceptions as sa_exc
 
 # py2.5 absolute imports will fix....
 logging = __import__('logging')
 
-# why is this in the "logging" module?
-class SADeprecationWarning(DeprecationWarning):
-    pass
-    
+# moved to sqlalchemy.exceptions.  this alias will be removed in 0.5.
+SADeprecationWarning = sa_exc.SADeprecationWarning
+
 rootlogger = logging.getLogger('sqlalchemy')
 rootlogger.setLevel(logging.WARN)
-warnings.filterwarnings("once", category=SADeprecationWarning)
+warnings.filterwarnings("once", category=sa_exc.SADeprecationWarning)
 
 default_enabled = False
 def default_logging(name):
@@ -47,14 +49,20 @@ def default_logging(name):
     if not default_enabled:
         default_enabled = True
         handler = logging.StreamHandler(sys.stdout)
-        handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(name)s %(message)s'))
+        handler.setFormatter(logging.Formatter(
+            '%(asctime)s %(levelname)s %(name)s %(message)s'))
         rootlogger.addHandler(handler)
 
 def _get_instance_name(instance):
-    # since getLogger() does not have any way of removing logger objects from memory,
-    # instance logging displays the instance id as a modulus of 16 to prevent endless memory growth
-    # also speeds performance as logger initialization is apparently slow
-    return instance.__class__.__module__ + "." + instance.__class__.__name__ + ".0x.." + hex(id(instance))[-2:]
+    # since getLogger() does not have any way of removing logger objects from
+    # memory, instance logging displays the instance id as a modulus of 16 to
+    # prevent endless memory growth also speeds performance as logger
+    # initialization is apparently slow
+    return "%s.%s.0x..%s" % (instance.__class__.__module__,
+                             instance.__class__.__name__,
+                             hex(id(instance))[-2:])
+    return (instance.__class__.__module__ + "." + instance.__class__.__name__ +
+            ".0x.." + hex(id(instance))[-2:])
 
 def class_logger(cls):
     return logging.getLogger(cls.__module__ + "." + cls.__name__)
@@ -83,13 +91,15 @@ def instance_logger(instance, echoflag=None):
     return l
 
 class echo_property(object):
-    __doc__ = """when ``True``, enable log output for this element.
+    __doc__ = """\
+    When ``True``, enable log output for this element.
+
 
-    This has the effect of setting the Python logging level for the
-    namespace of this element's class and object reference.  A value
-    of boolean ``True`` indicates that the loglevel ``logging.INFO`` will be
-    set for the logger, whereas the string value ``debug`` will set the loglevel
-    to ``logging.DEBUG``.
+    This has the effect of setting the Python logging level for the namespace
+    of this element's class and object reference.  A value of boolean ``True``
+    indicates that the loglevel ``logging.INFO`` will be set for the logger,
+    whereas the string value ``debug`` will set the loglevel to
+    ``logging.DEBUG``.
     """
 
     def __get__(self, instance, owner):
index c15ea3b159276cc3f5b98eef98dbc4251e6a42d7..2d1eebf1549b302ee5cd6d9b1eeb49556184c4cc 100644 (file)
@@ -7,11 +7,11 @@
 """Defines the [sqlalchemy.orm.mapper#Mapper] class, the central configurational
 unit which associates a class with a database table.
 
-This is a semi-private module; the main configurational API of the ORM is 
+This is a semi-private module; the main configurational API of the ORM is
 avaiable in [sqlalchemy.orm#].
 """
 
-import weakref, warnings
+import weakref
 from itertools import chain
 from sqlalchemy import sql, util, exceptions, logging
 from sqlalchemy.sql import expression, visitors, operators, util as sqlutil
@@ -76,7 +76,7 @@ class Mapper(object):
                  eager_defaults=False):
         """Construct a new mapper.
 
-        Mappers are normally constructed via the [sqlalchemy.orm#mapper()] 
+        Mappers are normally constructed via the [sqlalchemy.orm#mapper()]
         function.  See for details.
         """
 
@@ -118,7 +118,7 @@ class Mapper(object):
         self._eager_loaders = util.Set()
         self._row_translators = {}
         self._dependency_processors = []
-        
+
         # our 'polymorphic identity', a string name that when located in a result set row
         # indicates this Mapper should be used to construct the object instance for that row.
         self.polymorphic_identity = polymorphic_identity
@@ -129,7 +129,7 @@ class Mapper(object):
             self.polymorphic_fetch = (self.select_table is None) and 'select' or 'union'
         else:
             self.polymorphic_fetch = polymorphic_fetch
-        
+
         # a dictionary of 'polymorphic identity' names, associating those names with
         # Mappers that will be used to construct object instances upon a select operation.
         if _polymorphic_map is None:
@@ -153,7 +153,7 @@ class Mapper(object):
 
         self.__should_log_info = logging.is_info_enabled(self.logger)
         self.__should_log_debug = logging.is_debug_enabled(self.logger)
-        
+
         self._compile_class()
         self._compile_inheritance()
         self._compile_extensions()
@@ -192,13 +192,13 @@ class Mapper(object):
 
     def get_property(self, key, resolve_synonyms=False, raiseerr=True):
         """return a MapperProperty associated with the given key."""
-        
+
         self.compile()
         return self._get_property(key, resolve_synonyms=resolve_synonyms, raiseerr=raiseerr)
 
     def _get_property(self, key, resolve_synonyms=False, raiseerr=True):
         """private in-compilation version of get_property()."""
-        
+
         prop = self.__props.get(key, None)
         if resolve_synonyms:
             while isinstance(prop, SynonymProperty):
@@ -206,18 +206,18 @@ class Mapper(object):
         if prop is None and raiseerr:
             raise exceptions.InvalidRequestError("Mapper '%s' has no property '%s'" % (str(self), key))
         return prop
-    
+
     def iterate_properties(self):
         self.compile()
         return self.__props.itervalues()
     iterate_properties = property(iterate_properties, doc="returns an iterator of all MapperProperty objects.")
-    
+
     def properties(self):
         raise NotImplementedError("Public collection of MapperProperty objects is provided by the get_property() and iterate_properties accessors.")
     properties = property(properties)
-    
+
     compiled = property(lambda self:self.__props_init, doc="return True if this mapper is compiled")
-    
+
     def dispose(self):
         # disaable any attribute-based compilation
         self.__props_init = True
@@ -229,7 +229,7 @@ class Mapper(object):
             del self._class_state.mappers[self.entity_name]
         if not self._class_state.mappers:
             attributes.unregister_class(self.class_)
-        
+
     def compile(self):
         """Compile this mapper into its final internal format.
         """
@@ -241,7 +241,7 @@ class Mapper(object):
             # double-check inside mutex
             if self.__props_init:
                 return self
-                
+
             # initialize properties on all mappers
             for mapper in list(_mapper_registry):
                 if not mapper.__props_init:
@@ -283,7 +283,7 @@ class Mapper(object):
             for ext_obj in util.to_list(extension):
                 # local MapperExtensions have already instrumented the class
                 extlist.add(ext_obj)
-        
+
         if self.inherits is not None:
             for ext in self.inherits.extension:
                 if ext not in extlist:
@@ -296,11 +296,11 @@ class Mapper(object):
                 if ext not in extlist:
                     extlist.add(ext)
                     ext.instrument_class(self, self.class_)
-            
+
         self.extension = ExtensionCarrier()
         for ext in extlist:
             self.extension.append(ext)
-        
+
     def _compile_inheritance(self):
         """Determine if this Mapper inherits from another mapper, and
         if so calculates the mapped_table for this Mapper taking the
@@ -360,10 +360,10 @@ class Mapper(object):
                 self._identity_class = self.inherits._identity_class
             else:
                 self._identity_class = self.class_
-            
+
             if self.version_id_col is None:
                 self.version_id_col = self.inherits.version_id_col
-                
+
             if self.order_by is False:
                 self.order_by = self.inherits.order_by
             self.polymorphic_map = self.inherits.polymorphic_map
@@ -381,7 +381,7 @@ class Mapper(object):
                     raise exceptions.ArgumentError("Mapper '%s' specifies a polymorphic_identity of '%s', but no mapper in it's hierarchy specifies the 'polymorphic_on' column argument" % (str(self), self.polymorphic_identity))
                 self.polymorphic_map[self.polymorphic_identity] = self
             self._identity_class = self.class_
-            
+
         if self.mapped_table is None:
             raise exceptions.ArgumentError("Mapper '%s' does not have a mapped_table specified.  (Are you using the return value of table.create()?  It no longer has a return value.)" % str(self))
 
@@ -409,23 +409,23 @@ class Mapper(object):
 
         self._pks_by_table = {}
         self._cols_by_table = {}
-        
+
         all_cols = util.Set(chain(*[c2 for c2 in [col.proxy_set for col in [c for c in self._columntoproperty]]]))
         pk_cols = util.Set([c for c in all_cols if c.primary_key])
-        
+
         for t in util.Set(self.tables + [self.mapped_table]):
             self._all_tables.add(t)
             if t.primary_key and pk_cols.issuperset(t.primary_key):
                 # ordering is important since it determines the ordering of mapper.primary_key (and therefore query.get())
                 self._pks_by_table[t] = util.OrderedSet(t.primary_key).intersection(pk_cols)
             self._cols_by_table[t] = util.OrderedSet(t.c).intersection(all_cols)
-            
+
         if self.primary_key_argument:
             for k in self.primary_key_argument:
                 if k.table not in self._pks_by_table:
                     self._pks_by_table[k.table] = util.OrderedSet()
                 self._pks_by_table[k.table].add(k)
-                
+
         if self.mapped_table not in self._pks_by_table or len(self._pks_by_table[self.mapped_table]) == 0:
             raise exceptions.ArgumentError("Could not assemble any primary key columns for mapped table '%s'" % (self.mapped_table.name))
 
@@ -439,7 +439,7 @@ class Mapper(object):
             # multiple columns that all reference a common parent column.  it will also resolve the column
             # against the "mapped_table" of this mapper.
             self._equivalent_columns = self._get_equivalent_columns()
-        
+
             primary_key = expression.ColumnSet()
 
             for col in (self.primary_key_argument or self._pks_by_table[self.mapped_table]):
@@ -473,7 +473,7 @@ class Mapper(object):
                     else:
                         break
                 primary_key.add(c)
-                
+
             if len(primary_key) == 0:
                 raise exceptions.ArgumentError("Could not assemble any primary key columns for mapped table '%s'" % (self.mapped_table.name))
 
@@ -497,8 +497,8 @@ class Mapper(object):
         one another either by an established foreign key relationship
         or by a joined-table inheritance join.
 
-        This is used to determine the minimal set of primary key 
-        columns for the mapper, as well as when relating 
+        This is used to determine the minimal set of primary key
+        columns for the mapper, as well as when relating
         columns to those of a polymorphic selectable (i.e. a UNION of
         several mapped tables), as that selectable usually only contains
         one column in its columns clause out of a group of several which
@@ -508,12 +508,12 @@ class Mapper(object):
         to lists of equivalent columns, i.e.
 
         {
-            tablea.col1: 
+            tablea.col1:
                 set([tableb.col1, tablec.col1]),
             tablea.col2:
                 set([tabled.col2])
         }
-        
+
         """
 
         result = {}
@@ -545,7 +545,7 @@ class Mapper(object):
                     result[fk.column] = util.Set()
                 result[fk.column].add(equiv)
                 equivs(fk.column, recursive, col)
-                
+
         for column in (self.primary_key_argument or self._pks_by_table[self.mapped_table]):
             for col in column.proxy_set:
                 if not col.foreign_keys:
@@ -554,9 +554,9 @@ class Mapper(object):
                     result[col].add(col)
                 else:
                     equivs(col, util.Set(), col)
-                    
+
         return result
-    
+
     class _CompileOnAttr(PropComparator):
         """placeholder class attribute which fires mapper compilation on access"""
 
@@ -564,7 +564,7 @@ class Mapper(object):
             self.class_ = class_
             self.key = key
             self.existing_prop = getattr(class_, key, None)
-            
+
         def __getattribute__(self, key):
             cls = object.__getattribute__(self, 'class_')
             clskey = object.__getattribute__(self, 'key')
@@ -573,17 +573,19 @@ class Mapper(object):
                 return object.__getattribute__(self, key)
 
             class_mapper(cls)
-            
+
             if cls.__dict__.get(clskey) is self:
-                # FIXME: there should not be any scenarios where 
-                # a mapper compile leaves this CompileOnAttr in 
-                # place.  
-                warnings.warn(RuntimeWarning("Attribute '%s' on class '%s' was not replaced during mapper compilation operation" % (clskey, cls.__name__)))
+                # FIXME: there should not be any scenarios where
+                # a mapper compile leaves this CompileOnAttr in
+                # place.
+                util.warn(
+                    ("Attribute '%s' on class '%s' was not replaced during "
+                     "mapper compilation operation") % (clskey, cls.__name__))
                 # clean us up explicitly
                 delattr(cls, clskey)
-                
+
             return getattr(getattr(cls, clskey), key)
-            
+
     def _compile_properties(self):
 
         # object attribute names mapped to MapperProperty objects
@@ -615,14 +617,14 @@ class Mapper(object):
                 column.key not in self.include_properties):
                 self.__log("not including property %s" % (column.key))
                 continue
-            
+
             if (self.exclude_properties is not None and
                 column.key in self.exclude_properties):
                 self.__log("excluding property %s" % (column.key))
                 continue
 
             column_key = (self.column_prefix or '') + column.key
-                
+
             self._compile_property(column_key, column, init=False, setparent=True)
 
     def _adapt_inherited_property(self, key, prop):
@@ -639,13 +641,13 @@ class Mapper(object):
             column = columns[0]
             if not expression.is_column(column):
                 raise exceptions.ArgumentError("%s=%r is not an instance of MapperProperty or Column" % (key, prop))
-            
+
             prop = self.__props.get(key, None)
 
             if isinstance(prop, ColumnProperty):
-                # TODO: the "property already exists" case is still not well defined here.  
+                # TODO: the "property already exists" case is still not well defined here.
                 # assuming single-column, etc.
-                
+
                 if prop.parent is not self:
                     # existing ColumnProperty from an inheriting mapper.
                     # make a copy and append our column to it
@@ -684,7 +686,7 @@ class Mapper(object):
             if prop.map_column:
                 if not key in self.mapped_table.c:
                     raise exceptions.ArgumentError("Can't compile synonym '%s': no column on table '%s' named '%s'"  % (prop.name, self.mapped_table.description, key))
-                self._compile_property(prop.name, ColumnProperty(self.mapped_table.c[key]), init=init, setparent=setparent)    
+                self._compile_property(prop.name, ColumnProperty(self.mapped_table.c[key]), init=init, setparent=setparent)
         self.__props[key] = prop
 
         if setparent:
@@ -744,12 +746,12 @@ class Mapper(object):
             self.compile()
             if 'init_instance' in self.extension.methods:
                 self.extension.init_instance(self, class_, oldinit, instance, args, kwargs)
-        
+
         def on_exception(class_, oldinit, instance, args, kwargs):
             util.warn_exception(self.extension.init_failed, self, class_, oldinit, instance, args, kwargs)
 
         attributes.register_class(self.class_, extra_init=extra_init, on_exception=on_exception, deferred_scalar_loader=_load_scalar_attributes)
-        
+
         self._class_state = self.class_._class_state
         _mapper_registry[self] = True
 
@@ -830,18 +832,18 @@ class Mapper(object):
         Raise ``InvalidRequestError`` if a session cannot be retrieved
         from the extension chain.
         """
-        
+
         if 'get_session' in self.extension.methods:
             s = self.extension.get_session()
             if s is not EXT_CONTINUE:
                 return s
 
         raise exceptions.InvalidRequestError("No contextual Session is established.")
-            
+
     def instances(self, cursor, session, *mappers, **kwargs):
         """Return a list of mapped instances corresponding to the rows
         in a given ResultProxy.
-        
+
         DEPRECATED.
         """
 
@@ -906,19 +908,19 @@ class Mapper(object):
                 raise exceptions.UnmappedColumnError("Column '%s.%s' is not available, due to conflicting property '%s':%s" % (column.table.name, column.name, column.key, repr(prop)))
             else:
                 raise exceptions.UnmappedColumnError("No column %s.%s is configured on mapper %s..." % (column.table.name, column.name, str(self)))
-        
+
     def _get_state_attr_by_column(self, state, column):
         return self._get_col_to_prop(column).getattr(state, column)
-    
+
     def _set_state_attr_by_column(self, state, column, value):
         return self._get_col_to_prop(column).setattr(state, value, column)
-        
+
     def _get_attr_by_column(self, obj, column):
         return self._get_col_to_prop(column).getattr(obj._state, column)
 
     def _get_committed_attr_by_column(self, obj, column):
         return self._get_col_to_prop(column).getcommitted(obj._state, column)
-        
+
     def _set_attr_by_column(self, obj, column, value):
         self._get_col_to_prop(column).setattr(obj._state, column, value)
 
@@ -945,7 +947,7 @@ class Mapper(object):
                 self._save_obj([state], uowtransaction, postupdate=postupdate, post_update_cols=post_update_cols, single=True)
             return
 
-        # if session has a connection callable, 
+        # if session has a connection callable,
         # organize individual states with the connection to use for insert/update
         if 'connection_callable' in uowtransaction.mapper_flush_opts:
             connection_callable = uowtransaction.mapper_flush_opts['connection_callable']
@@ -953,7 +955,7 @@ class Mapper(object):
         else:
             connection = uowtransaction.transaction.connection(self)
             tups = [(state, connection, _state_has_identity(state)) for state in states]
-            
+
         if not postupdate:
             # call before_XXX extensions
             for state, connection, has_identity in tups:
@@ -1044,7 +1046,7 @@ class Mapper(object):
                                 if col in pks:
                                     params[col._label] = mapper._get_state_attr_by_column(state, col)
                                 continue
-                                
+
                             prop = mapper._columntoproperty[col]
                             (added, unchanged, deleted) = attributes.get_history(state, prop.key, passive=True)
                             if added:
@@ -1067,13 +1069,13 @@ class Mapper(object):
             if update:
                 mapper = table_to_mapper[table]
                 clause = sql.and_()
-                
+
                 for col in mapper._pks_by_table[table]:
                     clause.clauses.append(col == sql.bindparam(col._label, type_=col.type))
-                    
+
                 if mapper.version_id_col is not None and table.c.contains_column(mapper.version_id_col):
                     clause.clauses.append(mapper.version_id_col == sql.bindparam(mapper.version_id_col._label, type_=col.type))
-                    
+
                 statement = table.update(clause)
                 pks = mapper._pks_by_table[table]
                 def comparator(a, b):
@@ -1083,7 +1085,7 @@ class Mapper(object):
                             return x
                     return 0
                 update.sort(comparator)
-                
+
                 rows = 0
                 for rec in update:
                     (state, params, mapper, connection, value_params) = rec
@@ -1134,15 +1136,15 @@ class Mapper(object):
                 else:
                     if 'after_update' in mapper.extension.methods:
                         mapper.extension.after_update(mapper, connection, state.obj())
-    
+
     def _postfetch(self, uowtransaction, connection, table, state, resultproxy, params, value_params):
         """After an ``INSERT`` or ``UPDATE``, assemble newly generated
         values on an instance.  For columns which are marked as being generated
-        on the database side, set up a group-based "deferred" loader 
+        on the database side, set up a group-based "deferred" loader
         which will populate those attributes in one query when next accessed.
         """
 
-        postfetch_cols = util.Set(resultproxy.postfetch_cols()).union(util.Set(value_params.keys())) 
+        postfetch_cols = util.Set(resultproxy.postfetch_cols()).union(util.Set(value_params.keys()))
         deferred_props = []
 
         for c in self._cols_by_table[table]:
@@ -1151,7 +1153,7 @@ class Mapper(object):
                 deferred_props.append(prop.key)
             elif not c.primary_key and c.key in params and self._get_state_attr_by_column(state, c) != params[c.key]:
                 self._set_state_attr_by_column(state, c, params[c.key])
-        
+
         if deferred_props:
             if self.eager_defaults:
                 _instance_key = self._identity_key_from_state(state)
@@ -1242,9 +1244,9 @@ class Mapper(object):
             prop.register_dependencies(uowcommit)
         for dep in self._dependency_processors:
             dep.register_dependencies(uowcommit)
-            
+
     def cascade_iterator(self, type, state, recursive=None, halt_on=None):
-        """Iterate each element and its mapper in an object graph, 
+        """Iterate each element and its mapper in an object graph,
         for all relations that meet the given cascade rule.
 
         type
@@ -1258,7 +1260,7 @@ class Mapper(object):
         recursive
           Used by the function for internal context during recursive
           calls, leave as None.
-        
+
         the return value are object instances; this provides a strong
         reference so that they don't fall out of scope immediately.
         """
@@ -1281,7 +1283,7 @@ class Mapper(object):
     def _instance(self, context, row, result=None, skip_polymorphic=False, extension=None, only_load_props=None, refresh_instance=None):
         if not extension:
             extension = self.extension
-            
+
         if 'translate_row' in extension.methods:
             ret = extension.translate_row(self, context, row)
             if ret is not EXT_CONTINUE:
@@ -1296,13 +1298,13 @@ class Mapper(object):
                         context.attributes[('polymorphic_fetch', mapper)] = (self, [t for t in mapper.tables if t not in self.tables])
                     row = self.translate_row(mapper, row)
                     return mapper._instance(context, row, result=result, skip_polymorphic=True)
-        
-        # determine identity key 
+
+        # determine identity key
         if refresh_instance:
             identitykey = refresh_instance.dict['_instance_key']
         else:
             identitykey = self.identity_key_from_row(row)
-            
+
         session_identity_map = context.session.identity_map
 
         if identitykey in session_identity_map:
@@ -1314,7 +1316,7 @@ class Mapper(object):
 
             isnew = state.runid != context.runid
             currentload = not isnew
-            
+
             if not currentload and context.version_check and self.version_id_col and self._get_attr_by_column(instance, self.version_id_col) != row[self.version_id_col]:
                 raise exceptions.ConcurrentModificationError("Instance '%s' version of %s does not match %s" % (instance, self._get_attr_by_column(instance, self.version_id_col), row[self.version_id_col]))
         elif refresh_instance:
@@ -1328,7 +1330,7 @@ class Mapper(object):
         else:
             if self.__should_log_debug:
                 self.__log_debug("_instance(): identity key %s not in session" % str(identitykey))
-                
+
             if self.allow_null_pks:
                 for x in identitykey[1]:
                     if x is not None:
@@ -1349,21 +1351,21 @@ class Mapper(object):
                     attributes.manage(instance)
             else:
                 instance = attributes.new_instance(self.class_)
-                
+
             if self.__should_log_debug:
                 self.__log_debug("_instance(): created new instance %s identity %s" % (instance_str(instance), str(identitykey)))
-            
-            state = instance._state    
+
+            state = instance._state
             instance._entity_name = self.entity_name
             instance._instance_key = identitykey
             instance._sa_session_id = context.session.hash_key
             session_identity_map[identitykey] = instance
-        
+
         if currentload or context.populate_existing or self.always_refresh:
             if isnew:
                 state.runid = context.runid
                 context.progress.add(state)
-                
+
             if 'populate_instance' not in extension.methods or extension.populate_instance(self, context, row, instance, only_load_props=only_load_props, instancekey=identitykey, isnew=isnew) is EXT_CONTINUE:
                 self.populate_instance(context, instance, row, only_load_props=only_load_props, instancekey=identitykey, isnew=isnew)
 
@@ -1373,10 +1375,10 @@ class Mapper(object):
 #            if 'populate_instance' not in extension.methods or extension.populate_instance(self, context, row, instance, only_load_props=attrs, instancekey=identitykey, isnew=isnew) is EXT_CONTINUE:
 #                self.populate_instance(context, instance, row, only_load_props=attrs, instancekey=identitykey, isnew=isnew)
 #            context.partials.add((state, attrs))  <-- allow query.instances to commit the subset of attrs
-            
+
         if result is not None and ('append_result' not in extension.methods or extension.append_result(self, context, row, instance, result, instancekey=identitykey, isnew=isnew) is EXT_CONTINUE):
             result.append(instance)
-            
+
         return instance
 
     def translate_row(self, tomapper, row):
@@ -1386,7 +1388,7 @@ class Mapper(object):
         This can be used in conjunction with populate_instance to
         populate an instance using an alternate mapper.
         """
-        
+
         if tomapper in self._row_translators:
             # row translators are cached based on target mapper
             return self._row_translators[tomapper](row)
@@ -1400,7 +1402,7 @@ class Mapper(object):
 
         snapshot = selectcontext.path + (self,)
         # retrieve a set of "row population" functions derived from the MapperProperties attached
-        # to this Mapper.  These are keyed in the select context based primarily off the 
+        # to this Mapper.  These are keyed in the select context based primarily off the
         # "snapshot" of the stack, which represents a path from the lead mapper in the query to this one,
         # including relation() names.  the key also includes "self", and allows us to distinguish between
         # other mappers within our inheritance hierarchy
@@ -1420,12 +1422,12 @@ class Mapper(object):
                     existing_populators.append((prop.key, existingpop))
                 if post_proc is not None:
                     post_processors.append(post_proc)
-            
+
             # install a post processor for immediate post-load of joined-table inheriting mappers
             poly_select_loader = self._get_poly_select_loader(selectcontext, row)
             if poly_select_loader is not None:
                 post_processors.append(poly_select_loader)
-                
+
             selectcontext.attributes[('populators', self, snapshot, ispostselect)] = (new_populators, existing_populators)
             selectcontext.attributes[('post_processors', self, ispostselect)] = post_processors
 
@@ -1436,13 +1438,13 @@ class Mapper(object):
 
         if only_load_props:
             populators = [p for p in populators if p[0] in only_load_props]
-            
+
         for (key, populator) in populators:
             selectcontext.exec_with_path(self, key, populator, instance, row, ispostselect=ispostselect, isnew=isnew, **flags)
-            
+
         if self.non_primary:
             selectcontext.attributes[('populating_mapper', instance._state)] = self
-        
+
     def _post_instance(self, selectcontext, state):
         post_processors = selectcontext.attributes[('post_processors', self, None)]
         for p in post_processors:
@@ -1450,19 +1452,19 @@ class Mapper(object):
 
     def _get_poly_select_loader(self, selectcontext, row):
         """set up attribute loaders for 'select' and 'deferred' polymorphic loading.
-        
+
         this loading uses a second SELECT statement to load additional tables,
         either immediately after loading the main table or via a deferred attribute trigger.
         """
-        
+
         (hosted_mapper, needs_tables) = selectcontext.attributes.get(('polymorphic_fetch', self), (None, None))
-        
+
         if hosted_mapper is None or not needs_tables:
             return
-        
+
         cond, param_names = self._deferred_inheritance_condition(hosted_mapper, needs_tables)
         statement = sql.select(needs_tables, cond, use_labels=True)
-        
+
         if hosted_mapper.polymorphic_fetch == 'select':
             def post_execute(instance, **flags):
                 if self.__should_log_debug:
@@ -1478,7 +1480,7 @@ class Mapper(object):
             return post_execute
         elif hosted_mapper.polymorphic_fetch == 'deferred':
             from sqlalchemy.orm.strategies import DeferredColumnLoader
-            
+
             def post_execute(instance, **flags):
                 def create_statement(instance):
                     params = {}
@@ -1486,7 +1488,7 @@ class Mapper(object):
                         # use the "committed" (database) version to get query column values
                         params[bind] = self._get_committed_attr_by_column(instance, c)
                     return (statement, params)
-                
+
                 props = [prop for prop in [self._get_col_to_prop(col) for col in statement.inner_columns] if prop.key not in instance.__dict__]
                 keys = [p.key for p in props]
                 for prop in props:
@@ -1498,7 +1500,7 @@ class Mapper(object):
 
     def _deferred_inheritance_condition(self, base_mapper, needs_tables):
         base_mapper = base_mapper.primary_mapper()
-        
+
         def visit_binary(binary):
             leftcol = binary.left
             rightcol = binary.right
@@ -1520,7 +1522,7 @@ class Mapper(object):
             allconds.append(visitors.traverse(mapper.inherit_condition, clone=True, visit_binary=visit_binary))
 
         return sql.and_(*allconds), param_names
-            
+
 Mapper.logger = logging.class_logger(Mapper)
 
 
@@ -1529,7 +1531,7 @@ def has_identity(object):
 
 def _state_has_identity(state):
     return '_instance_key' in state.dict
-    
+
 def has_mapper(object):
     """Return True if the given object has had a mapper association
     set up, either through loading, or via insertion in a session.
@@ -1551,7 +1553,7 @@ def _load_scalar_attributes(instance, attribute_names):
             session = mapper.get_session()
         except exceptions.InvalidRequestError:
             raise exceptions.InvalidRequestError("Instance %s is not bound to a Session, and no contextual session is established; attribute refresh operation cannot proceed" % (instance.__class__))
-        
+
     if session.query(mapper)._get(instance._instance_key, refresh_instance=instance._state, only_load_props=attribute_names) is None:
         raise exceptions.InvalidRequestError("Could not refresh instance '%s'" % instance_str(instance))
 
@@ -1560,19 +1562,19 @@ def _state_mapper(state, entity_name=None):
 
 def object_mapper(object, entity_name=None, raiseerror=True):
     """Given an object, return the primary Mapper associated with the object instance.
-    
+
         object
             The object instance.
-            
+
         entity_name
-            Entity name of the mapper to retrieve, if the given instance is 
-            transient.  Otherwise uses the entity name already associated 
+            Entity name of the mapper to retrieve, if the given instance is
+            transient.  Otherwise uses the entity name already associated
             with the instance.
-            
+
         raiseerror
             Defaults to True: raise an ``InvalidRequestError`` if no mapper can
             be located.  If False, return None.
-            
+
     """
 
     try:
@@ -1586,7 +1588,7 @@ def object_mapper(object, entity_name=None, raiseerror=True):
 
 def class_mapper(class_, entity_name=None, compile=True):
     """Given a class and optional entity_name, return the primary Mapper associated with the key.
-    
+
     If no mapper can be located, raises ``InvalidRequestError``.
     """
 
index b2028fdcc6cae3de8c2fc89d41206dc8df888fe1..fd3fb7a77f0338799b8260d6dc710d1cdc492231 100644 (file)
@@ -4,7 +4,7 @@
 # This module is part of SQLAlchemy and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
-"""MapperProperty implementations.  
+"""MapperProperty implementations.
 
 This is a private module which defines the behavior of
 invidual ORM-mapped attributes.
@@ -18,9 +18,10 @@ from sqlalchemy.orm import session as sessionlib
 from sqlalchemy.orm.util import CascadeOptions
 from sqlalchemy.orm.interfaces import StrategizedProperty, PropComparator, MapperProperty
 from sqlalchemy.exceptions import ArgumentError
-import warnings
 
-__all__ = ['ColumnProperty', 'CompositeProperty', 'SynonymProperty', 'PropertyLoader', 'BackRef']
+__all__ = ('ColumnProperty', 'CompositeProperty', 'SynonymProperty',
+           'PropertyLoader', 'BackRef')
+
 
 class ColumnProperty(StrategizedProperty):
     """Describes an object attribute that corresponds to a table column."""
@@ -40,7 +41,7 @@ class ColumnProperty(StrategizedProperty):
         for col in columns:
             if not isinstance(col, ColumnElement):
                 raise ArgumentError('column_property() must be given a ColumnElement as its argument.  Try .label() or .as_scalar() for Selectables to fix this.')
-        
+
     def create_strategy(self):
         if self.deferred:
             return strategies.DeferredColumnLoader(self)
@@ -50,11 +51,16 @@ class ColumnProperty(StrategizedProperty):
     def do_init(self):
         super(ColumnProperty, self).do_init()
         if len(self.columns) > 1 and self.parent.primary_key.issuperset(self.columns):
-            warnings.warn(RuntimeWarning("On mapper %s, primary key column '%s' is being combined with distinct primary key column '%s' in attribute '%s'.  Use explicit properties to give each column its own mapped attribute name." % (str(self.parent), str(self.columns[1]), str(self.columns[0]), self.key)))
-        
+            util.warn(
+                ("On mapper %s, primary key column '%s' is being combined "
+                 "with distinct primary key column '%s' in attribute '%s'.  "
+                 "Use explicit properties to give each column its own mapped "
+                 "attribute name.") % (str(self.parent), str(self.columns[1]),
+                                       str(self.columns[0]), self.key))
+
     def copy(self):
         return ColumnProperty(deferred=self.deferred, group=self.group, *self.columns)
-    
+
     def getattr(self, state, column):
         return getattr(state.class_, self.key).impl.get(state)
 
@@ -63,7 +69,7 @@ class ColumnProperty(StrategizedProperty):
 
     def setattr(self, state, value, column):
         getattr(state.class_, self.key).impl.set(state, value, None)
-        
+
     def merge(self, session, source, dest, dont_load, _recursive):
         value = attributes.get_as_list(source._state, self.key, passive=True)
         if value:
@@ -71,14 +77,14 @@ class ColumnProperty(StrategizedProperty):
         else:
             # TODO: lazy callable should merge to the new instance
             dest._state.expire_attributes([self.key])
-            
+
     def get_col_value(self, column, value):
         return value
 
     class ColumnComparator(PropComparator):
         def clause_element(self):
             return self.prop.columns[0]
-            
+
         def operate(self, op, *other):
             return op(self.prop.columns[0], *other)
 
@@ -90,7 +96,7 @@ ColumnProperty.logger = logging.class_logger(ColumnProperty)
 
 class CompositeProperty(ColumnProperty):
     """subclasses ColumnProperty to provide composite type support."""
-    
+
     def __init__(self, class_, *columns, **kwargs):
         super(CompositeProperty, self).__init__(*columns, **kwargs)
         self.composite_class = class_
@@ -99,7 +105,7 @@ class CompositeProperty(ColumnProperty):
     def do_init(self):
         super(ColumnProperty, self).do_init()
         # TODO: similar PK check as ColumnProperty does ?
-        
+
     def copy(self):
         return CompositeProperty(deferred=self.deferred, group=self.group, composite_class=self.composite_class, *self.columns)
 
@@ -117,11 +123,11 @@ class CompositeProperty(ColumnProperty):
         if obj is None:
             obj = self.composite_class(*[None for c in self.columns])
             getattr(state.class_, self.key).impl.set(state, obj, None)
-            
+
         for a, b in zip(self.columns, value.__composite_values__()):
             if a is column:
                 setattr(obj, b, value)
-        
+
     def get_col_value(self, column, value):
         for a, b in zip(self.columns, value.__composite_values__()):
             if a is column:
@@ -146,7 +152,7 @@ class SynonymProperty(MapperProperty):
         self.name = name
         self.map_column=map_column
         self.instrument = None
-        
+
     def setup(self, querycontext, **kwargs):
         pass
 
@@ -169,7 +175,7 @@ class SynonymProperty(MapperProperty):
                         return s
                     return getattr(obj, self.name)
             self.instrument = SynonymProp()
-            
+
         sessionlib.register_attribute(class_, self.key, uselist=False, proxy_property=self.instrument, useobject=False, comparator=comparator)
 
     def merge(self, session, source, dest, _recursive):
@@ -214,7 +220,7 @@ class PropertyLoader(StrategizedProperty):
                 self.cascade = CascadeOptions("all, delete-orphan")
             else:
                 self.cascade = CascadeOptions("save-update, merge")
-        
+
         if self.passive_deletes == 'all' and ("delete" in self.cascade or "delete-orphan" in self.cascade):
             raise exceptions.ArgumentError("Can't set passive_deletes='all' in conjunction with 'delete' or 'delete-orphan' cascade")
 
@@ -255,9 +261,9 @@ class PropertyLoader(StrategizedProperty):
                             sql.exists([1], j & sql.and_(*[x==y for (x, y) in zip(self.prop.mapper.primary_key, self.prop.mapper.primary_key_from_instance(o))]))
                         )
                     return sql.and_(*clauses)
-            else:  
+            else:
                 return self.prop._optimized_compare(other)
-        
+
         def any(self, criterion=None, **kwargs):
             if not self.prop.uselist:
                 raise exceptions.InvalidRequestError("'any()' not implemented for scalar attributes. Use has().")
@@ -271,7 +277,7 @@ class PropertyLoader(StrategizedProperty):
                 else:
                     criterion = criterion & crit
             return sql.exists([1], j & criterion)
-        
+
         def has(self, criterion=None, **kwargs):
             if self.prop.uselist:
                 raise exceptions.InvalidRequestError("'has()' not implemented for collections.  Use any().")
@@ -285,7 +291,7 @@ class PropertyLoader(StrategizedProperty):
                 else:
                     criterion = criterion & crit
             return sql.exists([1], j & criterion)
-                
+
         def contains(self, other):
             if not self.prop.uselist:
                 raise exceptions.InvalidRequestError("'contains' not implemented for scalar attributes.  Use ==")
@@ -301,12 +307,12 @@ class PropertyLoader(StrategizedProperty):
         def __ne__(self, other):
             if self.prop.uselist and not hasattr(other, '__iter__'):
                 raise exceptions.InvalidRequestError("Can only compare a collection to an iterable object")
-                
+
             j = self.prop.primaryjoin
             if self.prop.secondaryjoin:
                 j = j & self.prop.secondaryjoin
             return ~sql.exists([1], j & sql.and_(*[x==y for (x, y) in zip(self.prop.mapper.primary_key, self.prop.mapper.primary_key_from_instance(other))]))
-            
+
     def compare(self, op, value, value_is_parent=False):
         if op == operators.eq:
             if value is None:
@@ -318,10 +324,10 @@ class PropertyLoader(StrategizedProperty):
                 return self._optimized_compare(value, value_is_parent=value_is_parent)
         else:
             return op(self.comparator, value)
-    
+
     def _optimized_compare(self, value, value_is_parent=False):
         return self._get_strategy(strategies.LazyLoader).lazy_clause(value, reverse_direction=not value_is_parent)
-    
+
     def private(self):
         return self.cascade.delete_orphan
     private = property(private)
@@ -383,7 +389,7 @@ class PropertyLoader(StrategizedProperty):
                     recursive.add(c)
 
                     # cascade using the mapper local to this object, so that its individual properties are located
-                    instance_mapper = object_mapper(c, entity_name=mapper.entity_name)  
+                    instance_mapper = object_mapper(c, entity_name=mapper.entity_name)
                     yield (c, instance_mapper)
                     for (c2, m) in instance_mapper.cascade_iterator(type, c._state, recursive):
                         yield (c2, m)
@@ -421,7 +427,11 @@ class PropertyLoader(StrategizedProperty):
         if not self.parent.concrete:
             for inheriting in self.parent.iterate_to_root():
                 if inheriting is not self.parent and inheriting._get_property(self.key, raiseerr=False):
-                    warnings.warn(RuntimeWarning("Warning: relation '%s' on mapper '%s' supercedes the same relation on inherited mapper '%s'; this can cause dependency issues during flush" % (self.key, self.parent, inheriting)))
+                    util.warn(
+                        ("Warning: relation '%s' on mapper '%s' supercedes "
+                         "the same relation on inherited mapper '%s'; this "
+                         "can cause dependency issues during flush") %
+                        (self.key, self.parent, inheriting))
 
         if self.association is not None:
             if isinstance(self.association, type):
@@ -441,17 +451,17 @@ class PropertyLoader(StrategizedProperty):
         if self.secondaryjoin is not None and self.secondary is None:
             raise exceptions.ArgumentError("Property '" + self.key + "' specified with secondary join condition but no secondary argument")
         # if join conditions were not specified, figure them out based on foreign keys
-        
+
         def _search_for_join(mapper, table):
             """find a join between the given mapper's mapped table and the given table.
-            will try the mapper's local table first for more specificity, then if not 
+            will try the mapper's local table first for more specificity, then if not
             found will try the more general mapped table, which in the case of inheritance
             is a join."""
             try:
                 return sql.join(mapper.local_table, table)
             except exceptions.ArgumentError, e:
                 return sql.join(mapper.mapped_table, table)
-        
+
         try:
             if self.secondary is not None:
                 if self.secondaryjoin is None:
@@ -513,7 +523,7 @@ class PropertyLoader(StrategizedProperty):
                 # or clauses related to those external tables dealt with.  see orm.relationships.ViewOnlyTest
                 if not col_is_part_of_mappings(binary.left) or not col_is_part_of_mappings(binary.right):
                     return
-                        
+
                 for f in binary.left.foreign_keys:
                     if f.references(binary.right.table):
                         self.foreign_keys.add(binary.left)
@@ -624,7 +634,7 @@ class PropertyLoader(StrategizedProperty):
             for c in list(self.remote_side):
                 if self.secondary and self.secondary.columns.contains_column(c):
                     continue
-                for equiv in [c] + (c in target_equivalents and list(target_equivalents[c]) or []): 
+                for equiv in [c] + (c in target_equivalents and list(target_equivalents[c]) or []):
                     corr = self.mapper.select_table.corresponding_column(equiv)
                     if corr:
                         self.remote_side.add(corr)
@@ -673,24 +683,24 @@ class PropertyLoader(StrategizedProperty):
 
     def get_join(self, parent, primary=True, secondary=True, polymorphic_parent=True):
         """return a join condition from the given parent mapper to this PropertyLoader's mapper.
-        
+
            The resulting ClauseElement object is cached and should not be modified directly.
-        
+
             parent
-              a mapper which has a relation() to this PropertyLoader.  A PropertyLoader can 
+              a mapper which has a relation() to this PropertyLoader.  A PropertyLoader can
               have multiple "parents" when its actual parent mapper has inheriting mappers.
-              
+
             primary
               include the primary join condition in the resulting join.
-              
+
             secondary
               include the secondary join condition in the resulting join.  If both primary
               and secondary are returned, they are joined via AND.
-              
+
             polymorphic_parent
               if True, use the parent's 'select_table' instead of its 'mapped_table' to produce the join.
         """
-        
+
         try:
             return self._parent_join_cache[(parent, primary, secondary, polymorphic_parent)]
         except KeyError:
@@ -725,7 +735,7 @@ PropertyLoader.logger = logging.class_logger(PropertyLoader)
 
 class BackRef(object):
     """Attached to a PropertyLoader to indicate a complementary reverse relationship.
-    
+
     Can optionally create the complementing PropertyLoader if one does not exist already."""
 
     def __init__(self, key, _prop=None, **kwargs):
@@ -736,9 +746,9 @@ class BackRef(object):
     def compile(self, prop):
         if self.prop:
             return
-        
+
         self.prop = prop
-        
+
         mapper = prop.mapper.primary_mapper()
         if mapper._get_property(self.key, raiseerr=False) is None:
             pj = self.kwargs.pop('primaryjoin', None)
@@ -747,12 +757,12 @@ class BackRef(object):
             parent = prop.parent.primary_mapper()
             self.kwargs.setdefault('viewonly', prop.viewonly)
             self.kwargs.setdefault('post_update', prop.post_update)
-                
+
             relation = PropertyLoader(parent, prop.secondary, pj, sj,
-                                      backref=BackRef(prop.key, _prop=prop), 
+                                      backref=BackRef(prop.key, _prop=prop),
                                       is_backref=True,
                                       **self.kwargs)
-                                      
+
             mapper._compile_property(self.key, relation);
 
             prop.reverse_property = mapper._get_property(self.key)
index 190530c5454f78a800e9fd4b9a9dfdeda7157276..c79b56ed5c8f282cfe18e58c262708041855e10f 100644 (file)
@@ -4,41 +4,42 @@
 # This module is part of SQLAlchemy and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
-"""Defines the [sqlalchemy.orm.query#Query] class, the central
-construct used by the ORM to construct database queries.
-
-The ``Query`` class should not be confused with the [sqlalchemy.sql.expression#Select]
-class, which defines database SELECT operations at the SQL (non-ORM) level.
-``Query`` differs from ``Select`` in that it returns ORM-mapped objects and interacts
-with an ORM session, whereas the ``Select`` construct interacts directly with the database
-to return iterable result sets.
-"""
+"""The Query class and support.
+
+Defines the [sqlalchemy.orm.query#Query] class, the central construct used by
+the ORM to construct database queries.
 
+The ``Query`` class should not be confused with the
+[sqlalchemy.sql.expression#Select] class, which defines database SELECT
+operations at the SQL (non-ORM) level.  ``Query`` differs from ``Select`` in
+that it returns ORM-mapped objects and interacts with an ORM session, whereas
+the ``Select`` construct interacts directly with the database to return
+iterable result sets.
+"""
 
+from itertools import chain
 from sqlalchemy import sql, util, exceptions, logging
 from sqlalchemy.sql import util as sql_util
 from sqlalchemy.sql import expression, visitors, operators
 from sqlalchemy.orm import mapper, object_mapper
 from sqlalchemy.orm.mapper import _state_mapper
 from sqlalchemy.orm import util as mapperutil
-from itertools import chain
-import warnings
 
 __all__ = ['Query', 'QueryContext']
 
 
 class Query(object):
     """Encapsulates the object-fetching operations provided by Mappers."""
-    
+
     def __init__(self, class_or_mapper, session=None, entity_name=None):
         if isinstance(class_or_mapper, type):
             self.mapper = mapper.class_mapper(class_or_mapper, entity_name=entity_name)
         else:
             self.mapper = class_or_mapper.compile()
         self.select_mapper = self.mapper.get_select_mapper().compile()
-        
+
         self._session = session
-            
+
         self._with_options = []
         self._lockmode = None
         self._extension = self.mapper.extension.copy()
@@ -68,31 +69,35 @@ class Query(object):
         self._primary_adapter=None
         self._only_load_props = None
         self._refresh_instance = None
-    
+
     def _no_criterion(self, meth):
         q = self._clone()
-        
+
         if q._criterion or q._statement or q._from_obj is not self.table:
-            warnings.warn(RuntimeWarning("Query.%s() being called on a Query with existing criterion; criterion is being ignored." % meth))
-            
+            util.warn(
+                ("Query.%s() being called on a Query with existing criterion; "
+                 "criterion is being ignored.") % meth)
+
         q._from_obj = self.table
         q._alias_ids = {}
         q._joinpoint = self.mapper
         q._statement = q._aliases = q._criterion = None
         q._order_by = q._group_by = q._distinct = False
         return q
-    
+
     def _no_statement(self, meth):
         q = self._clone()
         if q._statement:
-            raise exceptions.InvalidRequestError("Query.%s() being called on a Query with an existing full statement - can't apply criterion." % meth)
+            raise exceptions.InvalidRequestError(
+                ("Query.%s() being called on a Query with an existing full "
+                 "statement - can't apply criterion.") % meth)
         return q
-    
+
     def _clone(self):
         q = Query.__new__(Query)
         q.__dict__ = self.__dict__.copy()
         return q
-    
+
     def _get_session(self):
         if self._session is None:
             return self.mapper.get_session()
@@ -107,29 +112,29 @@ class Query(object):
         q = self._clone()
         q._current_path = path
         return q
-    
+
     def yield_per(self, count):
-        """yield only ``count`` rows at a time.
-        
-        WARNING: use this method with caution; if the same instance
-        is present in more than one batch of rows, end-user changes 
-        to attributes will be overwritten.  
-        In particular, it's usually impossible to use this setting with 
-        eagerly loaded collections (i.e. any lazy=False) since those 
-        collections will be cleared for a new load when encountered 
-        in a subsequent result batch.
+        """Yield only ``count`` rows at a time.
+
+        WARNING: use this method with caution; if the same instance is present
+        in more than one batch of rows, end-user changes to attributes will be
+        overwritten.
+
+        In particular, it's usually impossible to use this setting with
+        eagerly loaded collections (i.e. any lazy=False) since those
+        collections will be cleared for a new load when encountered in a
+        subsequent result batch.
         """
+
         q = self._clone()
         q._yield_per = count
         return q
-        
+
     def get(self, ident, **kwargs):
-        """Return an instance of the object based on the given
-        identifier, or None if not found.
+        """Return an instance of the object based on the given identifier, or None if not found.
 
-        The `ident` argument is a scalar or tuple of primary key
-        column values in the order of the table def's primary key
-        columns.
+        The `ident` argument is a scalar or tuple of primary key column values
+        in the order of the table def's primary key columns.
         """
 
         ret = self._extension.get(self, ident, **kwargs)
@@ -137,7 +142,7 @@ class Query(object):
             return ret
 
         # convert composite types to individual args
-        # TODO: account for the order of columns in the 
+        # TODO: account for the order of columns in the
         # ColumnProperty it corresponds to
         if hasattr(ident, '__composite_values__'):
             ident = ident.__composite_values__()
@@ -146,15 +151,14 @@ class Query(object):
         return self._get(key, ident, **kwargs)
 
     def load(self, ident, raiseerr=True, **kwargs):
-        """Return an instance of the object based on the given
-        identifier.
-
-        If not found, raises an exception.  The method will **remove
-        all pending changes** to the object already existing in the
-        Session.  The `ident` argument is a scalar or tuple of primary
-        key column values in the order of the table def's primary key
-        columns.
+        """Return an instance of the object based on the given identifier.
+
+        If not found, raises an exception.  The method will **remove all
+        pending changes** to the object already existing in the Session.  The
+        `ident` argument is a scalar or tuple of primary key column values in
+        the order of the table def's primary key columns.
         """
+
         ret = self._extension.load(self, ident, **kwargs)
         if ret is not mapper.EXT_CONTINUE:
             return ret
@@ -163,53 +167,54 @@ class Query(object):
         if instance is None and raiseerr:
             raise exceptions.InvalidRequestError("No instance found for identity %s" % repr(ident))
         return instance
-        
+
     def query_from_parent(cls, instance, property, **kwargs):
-        """return a newly constructed Query object, with criterion corresponding to 
-        a relationship to the given parent instance.
+        """Return a new Query with criterion corresponding to a parent instance.
 
-            instance
-                a persistent or detached instance which is related to class represented
-                by this query.
+        Return a newly constructed Query object, with criterion corresponding
+        to a relationship to the given parent instance.
 
-            property
-                string name of the property which relates this query's class to the 
-                instance. 
-                
-            \**kwargs
-                all extra keyword arguments are propigated to the constructor of
-                Query.
-                
+        instance
+          a persistent or detached instance which is related to class
+          represented by this query.
+
+         property
+           string name of the property which relates this query's class to the
+           instance.
+
+         \**kwargs
+           all extra keyword arguments are propigated to the constructor of
+           Query.
         """
-        
+
         mapper = object_mapper(instance)
         prop = mapper.get_property(property, resolve_synonyms=True)
         target = prop.mapper
         criterion = prop.compare(operators.eq, instance, value_is_parent=True)
         return Query(target, **kwargs).filter(criterion)
     query_from_parent = classmethod(query_from_parent)
-    
+
     def autoflush(self, setting):
         q = self._clone()
         q._autoflush = setting
         return q
-        
+
     def populate_existing(self):
-        """return a Query that will refresh all instances loaded.
-        
-        this includes all entities accessed from the database, including
+        """Return a Query that will refresh all instances loaded.
+
+        This includes all entities accessed from the database, including
         secondary entities, eagerly-loaded collection items.
-        
-        All changes present on entities which are already present in the session will 
-        be reset and the entities will all be marked "clean".
-        
+
+        All changes present on entities which are already present in the
+        session will be reset and the entities will all be marked "clean".
+
         This is essentially the en-masse version of load().
         """
-        
+
         q = self._clone()
         q._populate_existing = True
         return q
-        
+
     def with_parent(self, instance, property=None):
         """add a join criterion corresponding to a relationship to the given parent instance.
 
@@ -218,11 +223,11 @@ class Query(object):
                 by this query.
 
             property
-                string name of the property which relates this query's class to the 
+                string name of the property which relates this query's class to the
                 instance.  if None, the method will attempt to find a suitable property.
 
         currently, this method only works with immediate parent relationships, but in the
-        future may be enhanced to work across a chain of parent mappers.    
+        future may be enhanced to work across a chain of parent mappers.
         """
 
         from sqlalchemy.orm import properties
@@ -239,26 +244,26 @@ class Query(object):
 
     def add_entity(self, entity, alias=None, id=None):
         """add a mapped entity to the list of result columns to be returned.
-        
+
         This will have the effect of all result-returning methods returning a tuple
-        of results, the first element being an instance of the primary class for this 
+        of results, the first element being an instance of the primary class for this
         Query, and subsequent elements matching columns or entities which were
         specified via add_column or add_entity.
-        
+
         When adding entities to the result, its generally desireable to add
         limiting criterion to the query which can associate the primary entity
         of this Query along with the additional entities.  The Query selects
         from all tables with no joining criterion by default.
-        
+
             entity
                 a class or mapper which will be added to the results.
-                
+
             alias
                 a sqlalchemy.sql.Alias object which will be used to select rows.  this
                 will match the usage of the given Alias in filter(), order_by(), etc. expressions
-                
+
             id
-                a string ID matching that given to query.join() or query.outerjoin(); rows will be 
+                a string ID matching that given to query.join() or query.outerjoin(); rows will be
                 selected from the aliased join created via those methods.
         """
         q = self._clone()
@@ -270,49 +275,49 @@ class Query(object):
 
         q._entities = q._entities + [(entity, alias, id)]
         return q
-        
+
     def add_column(self, column, id=None):
-        """add a SQL ColumnElement to the list of result columns to be returned.
-        
-        This will have the effect of all result-returning methods returning a tuple
-        of results, the first element being an instance of the primary class for this 
-        Query, and subsequent elements matching columns or entities which were
-        specified via add_column or add_entity.
+        """Add a SQL ColumnElement to the list of result columns to be returned.
+
+        This will have the effect of all result-returning methods returning a
+        tuple of results, the first element being an instance of the primary
+        class for this Query, and subsequent elements matching columns or
+        entities which were specified via add_column or add_entity.
 
         When adding columns to the result, its generally desireable to add
         limiting criterion to the query which can associate the primary entity
-        of this Query along with the additional columns, if the column is based on a 
-        table or selectable that is not the primary mapped selectable.  The Query selects
-        from all tables with no joining criterion by default.
-        
-            column
-                a string column name or sql.ColumnElement to be added to the results.
-                
+        of this Query along with the additional columns, if the column is
+        based on a table or selectable that is not the primary mapped
+        selectable.  The Query selects from all tables with no joining
+        criterion by default.
+
+        column
+          a string column name or sql.ColumnElement to be added to the results.
         """
-        
+
         q = self._clone()
 
         # duck type to get a ClauseElement
         if hasattr(column, 'clause_element'):
             column = column.clause_element()
-        
-        # alias non-labeled column elements. 
+
+        # alias non-labeled column elements.
         if isinstance(column, sql.ColumnElement) and not hasattr(column, '_label'):
             column = column.label(None)
-            
+
         q._entities = q._entities + [(column, None, id)]
         return q
-        
+
     def options(self, *args):
         """Return a new Query object, applying the given list of
         MapperOptions.
         """
-        
+
         return self._options(False, *args)
 
     def _conditional_options(self, *args):
         return self._options(True, *args)
-        
+
     def _options(self, conditional, *args):
         q = self._clone()
         # most MapperOptions write to the '_attributes' dictionary,
@@ -327,7 +332,7 @@ class Query(object):
             for opt in opts:
                 opt.process_query(q)
         return q
-    
+
     def with_lockmode(self, mode):
         """Return a new Query object with the specified locking mode."""
         q = self._clone()
@@ -336,13 +341,13 @@ class Query(object):
 
     def params(self, *args, **kwargs):
         """add values for bind parameters which may have been specified in filter().
-        
+
         parameters may be specified using \**kwargs, or optionally a single dictionary
-        as the first positional argument.  The reason for both is that \**kwargs is 
+        as the first positional argument.  The reason for both is that \**kwargs is
         convenient, however some parameter dictionaries contain unicode keys in which case
         \**kwargs cannot be used.
         """
-        
+
         q = self._clone()
         if len(args) == 1:
             d = args[0]
@@ -352,25 +357,25 @@ class Query(object):
         q._params = q._params.copy()
         q._params.update(kwargs)
         return q
-        
+
     def filter(self, criterion):
         """apply the given filtering criterion to the query and return the newly resulting ``Query``
-        
+
         the criterion is any sql.ClauseElement applicable to the WHERE clause of a select.
         """
-        
+
         if isinstance(criterion, basestring):
             criterion = sql.text(criterion)
-            
+
         if criterion is not None and not isinstance(criterion, sql.ClauseElement):
             raise exceptions.ArgumentError("filter() argument must be of type sqlalchemy.sql.ClauseElement or string")
-        
-        
+
+
         if self._aliases is not None:
             criterion = self._aliases.adapt_clause(criterion)
         elif self.table not in self._get_joinable_tables():
             criterion = sql_util.ClauseAdapter(self._from_obj).traverse(criterion)
-            
+
         q = self._no_statement("filter")
         if q._criterion is not None:
             q._criterion = q._criterion & criterion
@@ -383,7 +388,7 @@ class Query(object):
 
         clauses = [self._joinpoint.get_property(key, resolve_synonyms=True).compare(operators.eq, value)
             for key, value in kwargs.iteritems()]
-        
+
         return self.filter(sql.and_(*clauses))
 
     def _get_joinable_tables(self):
@@ -395,27 +400,27 @@ class Query(object):
             visitors.traverse(self._from_obj, visit_join=visit_join, traverse_options={'column_collections':False, 'aliased_selectables':False})
             self.__joinable_tables = {self._from_obj : currenttables}
         return self.__joinable_tables[self._from_obj]
-        
+
     def _join_to(self, keys, outerjoin=False, start=None, create_aliases=True):
         if start is None:
             start = self._joinpoint
-            
+
         clause = self._from_obj
 
         currenttables = self._get_joinable_tables()
         adapt_criterion = self.table not in currenttables
-        
+
         mapper = start
         alias = self._aliases
         for key in util.to_list(keys):
             prop = mapper.get_property(key, resolve_synonyms=True)
             if prop._is_self_referential() and not create_aliases:
                 raise exceptions.InvalidRequestError("Self-referential query on '%s' property requires create_aliases=True argument." % str(prop))
-                
+
             if prop.select_table not in currenttables or create_aliases:
                 if prop.secondary:
                     if create_aliases:
-                        alias = mapperutil.PropertyAliasedClauses(prop, 
+                        alias = mapperutil.PropertyAliasedClauses(prop,
                             prop.get_join(mapper, primary=True, secondary=False),
                             prop.get_join(mapper, primary=False, secondary=True),
                             alias
@@ -432,7 +437,7 @@ class Query(object):
                         clause = clause.join(prop.select_table, prop.get_join(mapper, primary=False), isouter=outerjoin)
                 else:
                     if create_aliases:
-                        alias = mapperutil.PropertyAliasedClauses(prop, 
+                        alias = mapperutil.PropertyAliasedClauses(prop,
                             prop.get_join(mapper, primary=True, secondary=False),
                             None,
                             alias
@@ -450,9 +455,9 @@ class Query(object):
                 # TODO: this check is not strong enough for different paths to the same endpoint which
                 # does not use secondary tables
                 raise exceptions.InvalidRequestError("Can't join to property '%s'; a path to this table along a different secondary table already exists.  Use the `alias=True` argument to `join()`." % prop.key)
-                
+
             mapper = prop.mapper
-            
+
         if create_aliases:
             return (clause, mapper, alias)
         else:
@@ -528,17 +533,17 @@ class Query(object):
         """Execute the SQL ``avg()`` function against the given column."""
 
         return self._col_aggregate(col, sql.func.avg)
-    
+
     def order_by(self, criterion):
         """apply one or more ORDER BY criterion to the query and return the newly resulting ``Query``"""
 
         q = self._no_statement("order_by")
-        
+
         if self._aliases is not None:
             criterion = [expression._literal_as_text(o) for o in util.to_list(criterion) or []]
             criterion = self._aliases.adapt_list(criterion)
-        
-        if q._order_by is False:    
+
+        if q._order_by is False:
             q._order_by = util.to_list(criterion)
         else:
             q._order_by = q._order_by + util.to_list(criterion)
@@ -548,32 +553,32 @@ class Query(object):
         """apply one or more GROUP BY criterion to the query and return the newly resulting ``Query``"""
 
         q = self._no_statement("group_by")
-        if q._group_by is False:    
+        if q._group_by is False:
             q._group_by = util.to_list(criterion)
         else:
             q._group_by = q._group_by + util.to_list(criterion)
         return q
-    
+
     def having(self, criterion):
         """apply a HAVING criterion to the quer and return the newly resulting ``Query``."""
-        
+
         if isinstance(criterion, basestring):
             criterion = sql.text(criterion)
-            
+
         if criterion is not None and not isinstance(criterion, sql.ClauseElement):
             raise exceptions.ArgumentError("having() argument must be of type sqlalchemy.sql.ClauseElement or string")
-        
-        
+
+
         if self._aliases is not None:
             criterion = self._aliases.adapt_clause(criterion)
-            
+
         q = self._no_statement("having")
         if q._having is not None:
             q._having = q._having & criterion
         else:
             q._having = criterion
         return q
-        
+
     def join(self, prop, id=None, aliased=False, from_joinpoint=False):
         """create a join of this ``Query`` object's criterion
         to a relationship and return the newly resulting ``Query``.
@@ -583,11 +588,11 @@ class Query(object):
         """
 
         return self._join(prop, id=id, outerjoin=False, aliased=aliased, from_joinpoint=from_joinpoint)
-        
+
     def outerjoin(self, prop, id=None, aliased=False, from_joinpoint=False):
         """create a left outer join of this ``Query`` object's criterion
         to a relationship and return the newly resulting ``Query``.
-        
+
         'prop' may be a string property name or a list of string
         property names.
         """
@@ -600,20 +605,20 @@ class Query(object):
         q._from_obj = clause
         q._joinpoint = mapper
         q._aliases = aliases
-        
+
         a = aliases
         while a is not None:
             q._alias_ids.setdefault(a.mapper, []).append(a)
             q._alias_ids.setdefault(a.table, []).append(a)
             q._alias_ids.setdefault(a.alias, []).append(a)
             a = a.parentclauses
-            
+
         if id:
             q._alias_ids[id] = aliases
         return q
 
     def reset_joinpoint(self):
-        """return a new Query reset the 'joinpoint' of this Query reset 
+        """return a new Query reset the 'joinpoint' of this Query reset
         back to the starting mapper.  Subsequent generative calls will
         be constructed from the new joinpoint.
 
@@ -628,26 +633,26 @@ class Query(object):
 
 
     def select_from(self, from_obj):
-        """Set the `from_obj` parameter of the query and return the newly 
+        """Set the `from_obj` parameter of the query and return the newly
         resulting ``Query``.  This replaces the table which this Query selects
         from with the given table.
-        
-        
-        `from_obj` is a single table or selectable. 
+
+
+        `from_obj` is a single table or selectable.
         """
 
         new = self._no_criterion('select_from')
         if isinstance(from_obj, (tuple, list)):
             util.warn_deprecated("select_from() now accepts a single Selectable as its argument, which replaces any existing FROM criterion.")
             from_obj = from_obj[-1]
-            
+
         if isinstance(from_obj, expression._SelectBaseMixin):
             # alias SELECTs and unions
             from_obj = from_obj.alias()
-            
+
         new._from_obj = from_obj
         return new
-        
+
     def __getitem__(self, item):
         if isinstance(item, slice):
             start = item.start
@@ -701,37 +706,37 @@ class Query(object):
         This results in an execution of the underlying query.
         """
         return list(self)
-        
-    
+
+
     def from_statement(self, statement):
         """Execute the given SELECT statement and return results.
-        
-        This method bypasses all internal statement compilation, and the 
-        statement is executed without modification. 
-        
+
+        This method bypasses all internal statement compilation, and the
+        statement is executed without modification.
+
         The statement argument is either a string, a ``select()`` construct,
         or a ``text()`` construct, and should return the set of columns
         appropriate to the entity class represented by this ``Query``.
-        
+
         Also see the ``instances()`` method.
-        
+
         """
-        
+
         if isinstance(statement, basestring):
             statement = sql.text(statement)
         q = self._no_criterion('from_statement')
         q._statement = statement
         return q
-        
+
     def first(self):
         """Return the first result of this ``Query`` or None if the result doesn't contain any row.
 
         This results in an execution of the underlying query.
         """
 
-        if self._column_aggregate is not None: 
+        if self._column_aggregate is not None:
             return self._col_aggregate(*self._column_aggregate)
-        
+
         ret = list(self[0:1])
         if len(ret) > 0:
             return ret[0]
@@ -744,55 +749,55 @@ class Query(object):
         This results in an execution of the underlying query.
         """
 
-        if self._column_aggregate is not None: 
+        if self._column_aggregate is not None:
             return self._col_aggregate(*self._column_aggregate)
 
         ret = list(self[0:2])
-        
+
         if len(ret) == 1:
             return ret[0]
         elif len(ret) == 0:
             raise exceptions.InvalidRequestError('No rows returned for one()')
         else:
             raise exceptions.InvalidRequestError('Multiple rows returned for one()')
-    
+
     def __iter__(self):
         context = self._compile_context()
         context.statement.use_labels = True
         if self._autoflush and not self._populate_existing:
             self.session._autoflush()
         return self._execute_and_instances(context)
-    
+
     def _execute_and_instances(self, querycontext):
         result = self.session.execute(querycontext.statement, params=self._params, mapper=self.mapper, instance=self._refresh_instance)
         return self.iterate_instances(result, querycontext=querycontext)
 
     def instances(self, cursor, *mappers_or_columns, **kwargs):
         return list(self.iterate_instances(cursor, *mappers_or_columns, **kwargs))
-        
+
     def iterate_instances(self, cursor, *mappers_or_columns, **kwargs):
         session = self.session
 
         context = kwargs.pop('querycontext', None)
         if context is None:
             context = QueryContext(self)
-        
+
         context.runid = _new_runid()
-        
+
         mappers_or_columns = tuple(self._entities) + mappers_or_columns
         tuples = bool(mappers_or_columns)
 
         if self._primary_adapter:
             def main(context, row):
-                return self.select_mapper._instance(context, self._primary_adapter(row), None, 
+                return self.select_mapper._instance(context, self._primary_adapter(row), None,
                     extension=context.extension, only_load_props=context.only_load_props, refresh_instance=context.refresh_instance
                 )
         else:
             def main(context, row):
-                return self.select_mapper._instance(context, row, None, 
+                return self.select_mapper._instance(context, row, None,
                     extension=context.extension, only_load_props=context.only_load_props, refresh_instance=context.refresh_instance
                 )
-        
+
         if tuples:
             process = []
             process.append(main)
@@ -826,14 +831,14 @@ class Query(object):
 
         while True:
             context.progress = util.Set()
-            
+
             if self._yield_per:
                 fetch = cursor.fetchmany(self._yield_per)
                 if not fetch:
                     return
             else:
                 fetch = cursor.fetchall()
-                    
+
             if tuples:
                 rows = util.OrderedSet()
                 for row in fetch:
@@ -850,13 +855,13 @@ class Query(object):
             for ii in context.progress:
                 context.attributes.get(('populating_mapper', ii), _state_mapper(ii))._post_instance(context, ii)
                 ii.commit_all()
-            
+
             for row in rows:
                 yield row
-            
+
             if not self._yield_per:
                 break
-                
+
     def _get(self, key=None, ident=None, refresh_instance=None, lockmode=None, only_load_props=None):
         lockmode = lockmode or self._lockmode
         if not self._populate_existing and not refresh_instance and not self.mapper.always_refresh and lockmode is None:
@@ -864,7 +869,7 @@ class Query(object):
                 return self.session.identity_map[key]
             except KeyError:
                 pass
-            
+
         if ident is None:
             if key is not None:
                 ident = key[1]
@@ -883,7 +888,7 @@ class Query(object):
                 except IndexError:
                     raise exceptions.InvalidRequestError("Could not find enough values to formulate primary key for query.get(); primary key columns are %s" % ', '.join(["'%s'" % str(c) for c in self.primary_key_columns]))
             q = q.params(params)
-            
+
         if lockmode is not None:
             q = q.with_lockmode(lockmode)
         q = q._select_context_options(populate_existing=bool(refresh_instance), version_check=(lockmode is not None), only_load_props=only_load_props, refresh_instance=refresh_instance)
@@ -916,8 +921,8 @@ class Query(object):
 
     def _count(self):
         """Apply this query's criterion to a SELECT COUNT statement.
-        
-        this is the purely generative version which will become 
+
+        this is the purely generative version which will become
         the public method in version 0.5.
         """
 
@@ -934,11 +939,11 @@ class Query(object):
         if self._autoflush and not self._populate_existing:
             self.session._autoflush()
         return self.session.scalar(s, params=self._params, mapper=self.mapper)
-        
+
     def compile(self):
         """compiles and returns a SQL statement based on the criterion and conditions within this Query."""
         return self._compile_context().statement
-        
+
     def _compile_context(self):
 
         context = QueryContext(self)
@@ -947,12 +952,12 @@ class Query(object):
             self._statement.use_labels = True
             context.statement = self._statement
             return context
-        
+
         whereclause = self._criterion
 
         from_obj = self._from_obj
-        
-        # indicates if the "from" clause of the query does not include 
+
+        # indicates if the "from" clause of the query does not include
         # the normally mapped table, i.e. the user issued select_from(somestatement)
         # or similar.  all clauses which derive from the mapped table will need to
         # be adapted to be relative to the user-supplied selectable.
@@ -963,7 +968,7 @@ class Query(object):
         if not adapt_criterion and whereclause is not None and (self.mapper is not self.select_mapper):
             whereclause = sql_util.ClauseAdapter(from_obj, equivalents=self.select_mapper._get_equivalent_columns()).traverse(whereclause)
 
-        # TODO: mappers added via add_entity(), adapt their queries also, 
+        # TODO: mappers added via add_entity(), adapt their queries also,
         # if those mappers are polymorphic
 
         order_by = self._order_by
@@ -987,7 +992,7 @@ class Query(object):
             whereclause = sql.and_(whereclause, self.select_mapper.polymorphic_on.in_([m.polymorphic_identity for m in self.select_mapper.polymorphic_iterator()]))
 
         context.from_clause = from_obj
-        
+
         # give all the attached properties a chance to modify the query
         # TODO: doing this off the select_mapper.  if its the polymorphic mapper, then
         # it has no relations() on it.  should we compile those too into the query ?  (i.e. eagerloads)
@@ -1011,13 +1016,13 @@ class Query(object):
         if self._eager_loaders and self._nestable(**self._select_args()):
             # eager loaders are present, and the SELECT has limiting criterion
             # produce a "wrapped" selectable.
-            
+
             # ensure all 'order by' elements are ClauseElement instances
             # (since they will potentially be aliased)
             # locate all embedded Column clauses so they can be added to the
             # "inner" select statement where they'll be available to the enclosing
             # statement's "order by"
-            
+
             cf = util.Set()
             if order_by:
                 order_by = [expression._literal_as_text(o) for o in util.to_list(order_by) or []]
@@ -1029,9 +1034,9 @@ class Query(object):
                 cf = [from_obj.corresponding_column(c) or c for c in cf]
 
             s2 = sql.select(context.primary_columns + list(cf), whereclause, from_obj=context.from_clause, use_labels=True, correlate=False, order_by=util.to_list(order_by), **self._select_args())
-            
+
             s3 = s2.alias()
-                
+
             self._primary_adapter = mapperutil.create_row_adapter(s3, self.table)
 
             statement = sql.select([s3] + context.secondary_columns, for_update=for_update, use_labels=True)
@@ -1039,10 +1044,10 @@ class Query(object):
             if context.eager_joins:
                 eager_joins = sql_util.ClauseAdapter(s3).traverse(context.eager_joins)
                 statement.append_from(eager_joins, _copy_collection=False)
-            
+
             if order_by:
                 statement.append_order_by(*sql_util.ClauseAdapter(s3).copy_and_process(order_by))
-                
+
             statement.append_order_by(*context.eager_order_by)
         else:
             if adapt_criterion:
@@ -1055,16 +1060,16 @@ class Query(object):
 
                     if adapt_criterion:
                         order_by = sql_util.ClauseAdapter(from_obj).copy_and_process(order_by)
-                    
+
                 if self._distinct and order_by:
                     cf = util.Set()
                     for o in order_by:
                         cf.update(sql_util.find_columns(o))
                     for c in cf:
                         context.primary_columns.append(c)
-                
+
             statement = sql.select(context.primary_columns + context.secondary_columns, whereclause, from_obj=from_obj, use_labels=True, for_update=for_update, order_by=util.to_list(order_by), **self._select_args())
-            
+
             if context.eager_joins:
                 if adapt_criterion:
                     context.eager_joins = sql_util.ClauseAdapter(from_obj).traverse(context.eager_joins)
@@ -1074,9 +1079,9 @@ class Query(object):
                 if adapt_criterion:
                     context.eager_order_by = sql_util.ClauseAdapter(from_obj).copy_and_process(context.eager_order_by)
                 statement.append_order_by(*context.eager_order_by)
-                
+
         context.statement = statement
-                
+
         return context
 
     def _select_args(self):
@@ -1089,7 +1094,7 @@ class Query(object):
         """for tuples added via add_entity() or add_column(), attempt to locate
         an AliasedClauses object which should be used to formulate the query as well
         as to process result rows."""
-        
+
         (m, alias, alias_id) = m
         if alias is not None:
             return alias
@@ -1120,7 +1125,7 @@ class Query(object):
                 return aliases[0]
             else:
                 return None
-            
+
     def __log_debug(self, msg):
         self.logger.debug(msg)
 
@@ -1155,14 +1160,14 @@ class Query(object):
         if params is not None:
             q = q.params(params)
         return list(q)
-    
+
     def _legacy_select_from(self, from_obj):
         q = self._clone()
         if len(from_obj) > 1:
             raise exceptions.ArgumentError("Multiple-entry from_obj parameter no longer supported")
         q._from_obj = from_obj[0]
         return q
-        
+
     def _legacy_select_kwargs(self, **kwargs): #pragma: no cover
         q = self
         if "order_by" in kwargs and kwargs['order_by']:
@@ -1236,7 +1241,7 @@ class Query(object):
 
     def select_statement(self, statement, **params): #pragma: no cover
         """DEPRECATED.  Use query.from_statement(statement)"""
-        
+
         return self._select_statement(statement, params)
 
     def select_text(self, text, **params): #pragma: no cover
@@ -1261,7 +1266,7 @@ class Query(object):
         if only_load_props:
             self._only_load_props = util.Set(only_load_props)
         return self
-        
+
     def join_to(self, key): #pragma: no cover
         """DEPRECATED. use join() to create joins based on property names."""
 
@@ -1313,7 +1318,7 @@ class Query(object):
             if mapper_ in seen:
                 return None
             seen.add(mapper_)
-            
+
             prop = mapper_.get_property(key, resolve_synonyms=True, raiseerr=False)
             if prop is not None:
                 if isinstance(prop, properties.PropertyLoader):
@@ -1375,7 +1380,7 @@ class QueryContext(object):
         self.eager_joins = None
         self.options = query._with_options
         self.attributes = query._attributes.copy()
-    
+
     def exec_with_path(self, mapper, propkey, func, *args, **kwargs):
         oldpath = self.path
         self.path += (mapper.base_mapper, propkey)
@@ -1395,4 +1400,3 @@ def _new_runid():
         return _runid
     finally:
         _id_lock.release()
-
index b2eaa4221d7192daf6d2798198d001350f41ae7b..0bf9bea10620377abc027d272123d3d167f1d7d5 100644 (file)
@@ -25,9 +25,7 @@ classes usually have few or no public methods and are less guaranteed
 to stay the same in future releases.
 """
 
-import re
-import datetime
-import warnings
+import datetime, re
 from itertools import chain
 from sqlalchemy import util, exceptions
 from sqlalchemy.sql import operators, visitors
@@ -1464,7 +1462,9 @@ class ColumnCollection(util.OrderedProperties):
             existing = self[key]
             if not existing.shares_lineage(value):
                 table = getattr(existing, 'table', None) and existing.table.description
-                warnings.warn(RuntimeWarning("Column %r on table %r being replaced by another column with the same key.  Consider use_labels for select() statements."  % (key, table)))
+                util.warn(("Column %r on table %r being replaced by another "
+                           "column with the same key.  Consider use_labels "
+                           "for select() statements.")  % (key, table))
         util.OrderedProperties.__setitem__(self, key, value)
 
     def remove(self, column):
index d2f1e9ad22673151be3961b15eb5300ea3ef58ca..492fc985d88b4a61b6f9da303f3c9818c3007ff7 100644 (file)
@@ -23,7 +23,6 @@ __all__ = [ 'TypeEngine', 'TypeDecorator', 'AbstractType',
 
 import inspect
 import datetime as dt
-import warnings
 
 from sqlalchemy import exceptions
 from sqlalchemy.util import pickle, Decimal as _python_Decimal
@@ -370,7 +369,8 @@ class String(Concatenable, TypeEngine):
                     return value.encode(dialect.encoding)
                 elif assert_unicode and not isinstance(value, (unicode, NoneType)):
                     if assert_unicode == 'warn':
-                        warnings.warn(RuntimeWarning("Unicode type received non-unicode bind param value %r" % value))
+                        util.warn("Unicode type received non-unicode bind "
+                                  "param value %r" % value)
                         return value
                     else:
                         raise exceptions.InvalidRequestError("Unicode type received non-unicode bind param value %r" % value)
index 5c391ac3dd73cc93640abacf4db1ce2b04d2c388..05990e4ed6d8106c88b0e759c4378e339923e38e 100644 (file)
@@ -7,7 +7,7 @@
 import itertools, sys, warnings, sets, weakref
 import __builtin__
 
-from sqlalchemy import exceptions, logging
+from sqlalchemy import exceptions
 
 try:
     import thread, threading
@@ -45,9 +45,8 @@ try:
 except ImportError:
     def Decimal(arg):
         if Decimal.warn:
-            warnings.warn(RuntimeWarning(
-                "True Decimal types not available on this Python, "
-                "falling back to floats."))
+            warn("True Decimal types not available on this Python, "
+                "falling back to floats.")
             Decimal.warn = False
         return float(arg)
     Decimal.warn = True
@@ -241,7 +240,7 @@ def warn_exception(func, *args, **kwargs):
     try:
         return func(*args, **kwargs)
     except:
-        warnings.warn(RuntimeWarning("%s('%s') ignored" % sys.exc_info()[0:2]))
+        warn("%s('%s') ignored" % sys.exc_info()[0:2])
 
 class SimpleProperty(object):
     """A *default* property accessor."""
@@ -839,18 +838,35 @@ class ScopedRegistry(object):
     def _get_key(self):
         return self.scopefunc()
 
+def warn(msg):
+    if isinstance(msg, basestring):
+        warnings.warn(msg, exceptions.SAWarning, stacklevel=3)
+    else:
+        warnings.warn(msg, stacklevel=3)
 
 def warn_deprecated(msg):
-    warnings.warn(logging.SADeprecationWarning(msg), stacklevel=3)
+    warnings.warn(msg, exceptions.SADeprecationWarning, stacklevel=3)
 
 def deprecated(func, message=None, add_deprecation_to_docstring=True):
+    """Decorates a function and issues a deprecation warning on use.
+
+    message
+      If provided, issue message in the warning.  A sensible default
+      is used if not provided.
+
+    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 message is not None:
         warning = message % dict(func=func.__name__)
     else:
         warning = "Call to deprecated function %s" % func.__name__
 
     def func_with_warning(*args, **kwargs):
-        warnings.warn(logging.SADeprecationWarning(warning),
+        warnings.warn(exceptions.SADeprecationWarning(warning),
                       stacklevel=2)
         return func(*args, **kwargs)