summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authorHajime Nakagami <nakagami@gmail.com>2013-04-06 17:45:42 +0900
committerHajime Nakagami <nakagami@gmail.com>2013-04-06 17:45:42 +0900
commitb4da894d02f2e99f0a604ad45f6c0e5058392d81 (patch)
tree1eb9887480bec5224924e379b2e0f03d10ce8ebb /lib/sqlalchemy
parent7a4945ebdd1358502e1872560b97f1344b8c53db (diff)
parent6bdd3bb93fd18a4aec54ee2a836875a922dcaab3 (diff)
downloadsqlalchemy-b4da894d02f2e99f0a604ad45f6c0e5058392d81.tar.gz
merge from default
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/__init__.py2
-rw-r--r--lib/sqlalchemy/dialects/__init__.py1
-rw-r--r--lib/sqlalchemy/dialects/firebird/kinterbasdb.py2
-rw-r--r--lib/sqlalchemy/dialects/mssql/pymssql.py4
-rw-r--r--lib/sqlalchemy/dialects/mssql/pyodbc.py2
-rw-r--r--lib/sqlalchemy/dialects/mysql/base.py1
-rw-r--r--lib/sqlalchemy/dialects/oracle/cx_oracle.py2
-rw-r--r--lib/sqlalchemy/dialects/postgresql/base.py12
-rw-r--r--lib/sqlalchemy/dialects/postgresql/hstore.py5
-rw-r--r--lib/sqlalchemy/dialects/postgresql/pg8000.py2
-rw-r--r--lib/sqlalchemy/dialects/postgresql/psycopg2.py2
-rw-r--r--lib/sqlalchemy/dialects/sybase/pyodbc.py2
-rw-r--r--lib/sqlalchemy/engine/default.py6
-rw-r--r--lib/sqlalchemy/orm/attributes.py43
-rw-r--r--lib/sqlalchemy/orm/interfaces.py10
-rw-r--r--lib/sqlalchemy/orm/mapper.py18
-rw-r--r--lib/sqlalchemy/orm/persistence.py2
-rw-r--r--lib/sqlalchemy/orm/properties.py8
-rw-r--r--lib/sqlalchemy/orm/relationships.py27
-rw-r--r--lib/sqlalchemy/orm/util.py37
-rw-r--r--lib/sqlalchemy/pool.py4
-rw-r--r--lib/sqlalchemy/schema.py19
-rw-r--r--lib/sqlalchemy/sql/compiler.py17
-rw-r--r--lib/sqlalchemy/sql/expression.py139
-rw-r--r--lib/sqlalchemy/sql/util.py7
-rw-r--r--lib/sqlalchemy/testing/assertsql.py2
-rw-r--r--lib/sqlalchemy/testing/plugin/noseplugin.py8
-rw-r--r--lib/sqlalchemy/testing/profiling.py4
-rw-r--r--lib/sqlalchemy/testing/runner.py10
-rw-r--r--lib/sqlalchemy/testing/schema.py27
-rw-r--r--lib/sqlalchemy/testing/suite/test_insert.py4
-rw-r--r--lib/sqlalchemy/testing/suite/test_reflection.py10
-rw-r--r--lib/sqlalchemy/testing/util.py3
-rw-r--r--lib/sqlalchemy/types.py7
-rw-r--r--lib/sqlalchemy/util/__init__.py16
-rw-r--r--lib/sqlalchemy/util/_collections.py39
-rw-r--r--lib/sqlalchemy/util/compat.py159
-rw-r--r--lib/sqlalchemy/util/langhelpers.py28
-rw-r--r--lib/sqlalchemy/util/topological.py3
39 files changed, 376 insertions, 318 deletions
diff --git a/lib/sqlalchemy/__init__.py b/lib/sqlalchemy/__init__.py
index 2352f1308..6e924ea9d 100644
--- a/lib/sqlalchemy/__init__.py
+++ b/lib/sqlalchemy/__init__.py
@@ -120,7 +120,7 @@ from .engine import create_engine, engine_from_config
__all__ = sorted(name for name, obj in locals().items()
if not (name.startswith('_') or _inspect.ismodule(obj)))
-__version__ = '0.8.0'
+__version__ = '0.8.1'
del _inspect, sys
diff --git a/lib/sqlalchemy/dialects/__init__.py b/lib/sqlalchemy/dialects/__init__.py
index fbbff153c..7f5d34707 100644
--- a/lib/sqlalchemy/dialects/__init__.py
+++ b/lib/sqlalchemy/dialects/__init__.py
@@ -18,7 +18,6 @@ __all__ = (
from .. import util
-
def _auto_fn(name):
"""default dialect importer.
diff --git a/lib/sqlalchemy/dialects/firebird/kinterbasdb.py b/lib/sqlalchemy/dialects/firebird/kinterbasdb.py
index 206dbf38b..d581f799a 100644
--- a/lib/sqlalchemy/dialects/firebird/kinterbasdb.py
+++ b/lib/sqlalchemy/dialects/firebird/kinterbasdb.py
@@ -50,8 +50,8 @@ __ http://kinterbasdb.sourceforge.net/dist_docs/usage.html#special_issue_concurr
from .base import FBDialect, FBExecutionContext
from ... import util, types as sqltypes
-from ...util.compat import decimal
from re import match
+import decimal
class _FBNumeric_kinterbasdb(sqltypes.Numeric):
diff --git a/lib/sqlalchemy/dialects/mssql/pymssql.py b/lib/sqlalchemy/dialects/mssql/pymssql.py
index 6f6d3b01f..b916612fb 100644
--- a/lib/sqlalchemy/dialects/mssql/pymssql.py
+++ b/lib/sqlalchemy/dialects/mssql/pymssql.py
@@ -54,7 +54,7 @@ class MSDialect_pymssql(MSDialect):
module = __import__('pymssql')
# pymmsql doesn't have a Binary method. we use string
# TODO: monkeypatching here is less than ideal
- module.Binary = str
+ module.Binary = lambda x: x if hasattr(x, 'decode') else str(x)
client_ver = tuple(int(x) for x in module.__version__.split("."))
if client_ver < (1, ):
@@ -85,6 +85,8 @@ class MSDialect_pymssql(MSDialect):
def is_disconnect(self, e, connection, cursor):
for msg in (
+ "Adaptive Server connection timed out",
+ "message 20003", # connection timeout
"Error 10054",
"Not connected to any MS SQL server",
"Connection is closed"
diff --git a/lib/sqlalchemy/dialects/mssql/pyodbc.py b/lib/sqlalchemy/dialects/mssql/pyodbc.py
index 8854d1caa..beb6066f5 100644
--- a/lib/sqlalchemy/dialects/mssql/pyodbc.py
+++ b/lib/sqlalchemy/dialects/mssql/pyodbc.py
@@ -114,7 +114,7 @@ for unix + PyODBC.
from .base import MSExecutionContext, MSDialect
from ...connectors.pyodbc import PyODBCConnector
from ... import types as sqltypes, util
-from ...util.compat import decimal
+import decimal
class _MSNumeric_pyodbc(sqltypes.Numeric):
diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py
index 62598ad00..f83020d93 100644
--- a/lib/sqlalchemy/dialects/mysql/base.py
+++ b/lib/sqlalchemy/dialects/mysql/base.py
@@ -2409,7 +2409,6 @@ class MySQLTableDefinitionParser(object):
state.constraints.append(spec)
else:
pass
-
return state
def _parse_constraints(self, line):
diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
index fd9fea878..b8f7439f5 100644
--- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py
+++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
@@ -190,7 +190,7 @@ from ...engine import result as _result
from sqlalchemy import types as sqltypes, util, exc, processors
import random
import collections
-from sqlalchemy.util.compat import decimal
+import decimal
import re
diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py
index a7a9e65ce..c59caff8d 100644
--- a/lib/sqlalchemy/dialects/postgresql/base.py
+++ b/lib/sqlalchemy/dialects/postgresql/base.py
@@ -1030,6 +1030,15 @@ class PGCompiler(compiler.SQLCompiler):
field, self.process(expr))
+ def visit_substring_func(self, func, **kw):
+ s = self.process(func.clauses.clauses[0], **kw)
+ start = self.process(func.clauses.clauses[1], **kw)
+ if len(func.clauses.clauses) > 2:
+ length = self.process(func.clauses.clauses[2], **kw)
+ return "SUBSTRING(%s FROM %s FOR %s)" % (s, start, length)
+ else:
+ return "SUBSTRING(%s FROM %s)" % (s, start)
+
class PGDDLCompiler(compiler.DDLCompiler):
def get_column_specification(self, column, **kwargs):
colspec = self.preparer.format_column(column)
@@ -1042,8 +1051,7 @@ class PGDDLCompiler(compiler.DDLCompiler):
(
isinstance(column.default, schema.Sequence) and
column.default.optional
- )
- ):
+ )):
if isinstance(impl_type, sqltypes.BigInteger):
colspec += " BIGSERIAL"
else:
diff --git a/lib/sqlalchemy/dialects/postgresql/hstore.py b/lib/sqlalchemy/dialects/postgresql/hstore.py
index 157e03fd5..e555a1afd 100644
--- a/lib/sqlalchemy/dialects/postgresql/hstore.py
+++ b/lib/sqlalchemy/dialects/postgresql/hstore.py
@@ -6,7 +6,7 @@
import re
-from .base import ARRAY
+from .base import ARRAY, ischema_names
from ... import types as sqltypes
from ...sql import functions as sqlfunc
from ...sql.operators import custom_op
@@ -276,6 +276,9 @@ class HSTORE(sqltypes.Concatenable, sqltypes.TypeEngine):
return process
+ischema_names['hstore'] = HSTORE
+
+
class hstore(sqlfunc.GenericFunction):
"""Construct an hstore value within a SQL expression using the
Postgresql ``hstore()`` function.
diff --git a/lib/sqlalchemy/dialects/postgresql/pg8000.py b/lib/sqlalchemy/dialects/postgresql/pg8000.py
index 214db348c..0e503746c 100644
--- a/lib/sqlalchemy/dialects/postgresql/pg8000.py
+++ b/lib/sqlalchemy/dialects/postgresql/pg8000.py
@@ -27,7 +27,7 @@ yet.
"""
from ... import util, exc
-from ...util.compat import decimal
+import decimal
from ... import processors
from ... import types as sqltypes
from .base import PGDialect, \
diff --git a/lib/sqlalchemy/dialects/postgresql/psycopg2.py b/lib/sqlalchemy/dialects/postgresql/psycopg2.py
index 649a95ee6..1f118067f 100644
--- a/lib/sqlalchemy/dialects/postgresql/psycopg2.py
+++ b/lib/sqlalchemy/dialects/postgresql/psycopg2.py
@@ -147,7 +147,7 @@ import re
import logging
from ... import util, exc
-from ...util.compat import decimal
+import decimal
from ... import processors
from ...engine import result as _result
from ...sql import expression
diff --git a/lib/sqlalchemy/dialects/sybase/pyodbc.py b/lib/sqlalchemy/dialects/sybase/pyodbc.py
index 644f4edb1..283c60da3 100644
--- a/lib/sqlalchemy/dialects/sybase/pyodbc.py
+++ b/lib/sqlalchemy/dialects/sybase/pyodbc.py
@@ -36,7 +36,7 @@ from sqlalchemy.dialects.sybase.base import SybaseDialect,\
SybaseExecutionContext
from sqlalchemy.connectors.pyodbc import PyODBCConnector
from sqlalchemy import types as sqltypes, processors
-from sqlalchemy.util.compat import decimal
+import decimal
class _SybNumeric_pyodbc(sqltypes.Numeric):
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index 1db0f2ce4..abb9f0fc3 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -34,6 +34,10 @@ class DefaultDialect(interfaces.Dialect):
preparer = compiler.IdentifierPreparer
supports_alter = True
+ # the first value we'd get for an autoincrement
+ # column.
+ default_sequence_base = 1
+
# most DBAPIs happy with this for execute().
# not cx_oracle.
execute_sequence_format = tuple
@@ -679,7 +683,7 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
lastrowid = proc(lastrowid)
self.inserted_primary_key = [
- c is autoinc_col and lastrowid or v
+ lastrowid if c is autoinc_col else v
for c, v in zip(
table.primary_key,
self.inserted_primary_key)
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py
index 891aef862..3eda127fd 100644
--- a/lib/sqlalchemy/orm/attributes.py
+++ b/lib/sqlalchemy/orm/attributes.py
@@ -174,6 +174,49 @@ class QueryableAttribute(interfaces._MappedAttribute,
# TODO: conditionally attach this method based on clause_element ?
return self
+
+ @util.memoized_property
+ def info(self):
+ """Return the 'info' dictionary for the underlying SQL element.
+
+ The behavior here is as follows:
+
+ * If the attribute is a column-mapped property, i.e.
+ :class:`.ColumnProperty`, which is mapped directly
+ to a schema-level :class:`.Column` object, this attribute
+ will return the :attr:`.SchemaItem.info` dictionary associated
+ with the core-level :class:`.Column` object.
+
+ * If the attribute is a :class:`.ColumnProperty` but is mapped to
+ any other kind of SQL expression other than a :class:`.Column`,
+ the attribute will refer to the :attr:`.MapperProperty.info` dictionary
+ associated directly with the :class:`.ColumnProperty`, assuming the SQL
+ expression itself does not have it's own ``.info`` attribute
+ (which should be the case, unless a user-defined SQL construct
+ has defined one).
+
+ * If the attribute refers to any other kind of :class:`.MapperProperty`,
+ including :class:`.RelationshipProperty`, the attribute will refer
+ to the :attr:`.MapperProperty.info` dictionary associated with
+ that :class:`.MapperProperty`.
+
+ * To access the :attr:`.MapperProperty.info` dictionary of the :class:`.MapperProperty`
+ unconditionally, including for a :class:`.ColumnProperty` that's
+ associated directly with a :class:`.schema.Column`, the attribute
+ can be referred to using :attr:`.QueryableAttribute.property`
+ attribute, as ``MyClass.someattribute.property.info``.
+
+ .. versionadded:: 0.8.0
+
+ .. seealso::
+
+ :attr:`.SchemaItem.info`
+
+ :attr:`.MapperProperty.info`
+
+ """
+ return self.comparator.info
+
@util.memoized_property
def parent(self):
"""Return an inspection instance representing the parent.
diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py
index b3f7baf0f..62cdb2710 100644
--- a/lib/sqlalchemy/orm/interfaces.py
+++ b/lib/sqlalchemy/orm/interfaces.py
@@ -209,6 +209,12 @@ class MapperProperty(_MappedAttribute, _InspectionAttr):
.. versionadded:: 0.8 Added support for .info to all
:class:`.MapperProperty` subclasses.
+ .. seealso::
+
+ :attr:`.QueryableAttribute.info`
+
+ :attr:`.SchemaItem.info`
+
"""
return {}
@@ -390,6 +396,10 @@ class PropComparator(operators.ColumnOperators):
return self.__class__(self.prop, self._parentmapper, adapter)
+ @util.memoized_property
+ def info(self):
+ return self.property.info
+
@staticmethod
def any_op(a, b, **kwargs):
return a.any(b, **kwargs)
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 4e7b4d272..d258a20b6 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -2002,10 +2002,20 @@ class Mapper(_InspectionAttr):
@_memoized_configured_property
def _sorted_tables(self):
table_to_mapper = {}
+
for mapper in self.base_mapper.self_and_descendants:
for t in mapper.tables:
table_to_mapper.setdefault(t, mapper)
+ extra_dependencies = []
+ for table, mapper in table_to_mapper.items():
+ super_ = mapper.inherits
+ if super_:
+ extra_dependencies.extend([
+ (super_table, table)
+ for super_table in super_.tables
+ ])
+
def skip(fk):
# attempt to skip dependencies that are not
# significant to the inheritance chain
@@ -2017,7 +2027,7 @@ class Mapper(_InspectionAttr):
if parent is not None and \
dep is not None and \
dep is not parent and \
- dep.inherit_condition is not None:
+ dep.inherit_condition is not None:
cols = set(sql_util.find_columns(dep.inherit_condition))
if parent.inherit_condition is not None:
cols = cols.union(sql_util.find_columns(
@@ -2028,7 +2038,9 @@ class Mapper(_InspectionAttr):
return False
sorted_ = sql_util.sort_tables(table_to_mapper.iterkeys(),
- skip_fn=skip)
+ skip_fn=skip,
+ extra_dependencies=extra_dependencies)
+
ret = util.OrderedDict()
for t in sorted_:
ret[t] = table_to_mapper[t]
@@ -2224,7 +2236,7 @@ def _event_on_resurrect(state):
state, state.dict, col, val)
-class _ColumnMapping(util.py25_dict):
+class _ColumnMapping(dict):
"""Error reporting helper for mapper._columntoproperty."""
def __init__(self, mapper):
diff --git a/lib/sqlalchemy/orm/persistence.py b/lib/sqlalchemy/orm/persistence.py
index d9dfff77d..e225a7c83 100644
--- a/lib/sqlalchemy/orm/persistence.py
+++ b/lib/sqlalchemy/orm/persistence.py
@@ -154,7 +154,7 @@ def _organize_states_for_save(base_mapper, states, uowtransaction):
# with the same identity key already exists as persistent.
# convert to an UPDATE if so.
if not has_identity and \
- instance_key in uowtransaction.session.identity_map:
+ instance_key in uowtransaction.session.identity_map:
instance = \
uowtransaction.session.identity_map[instance_key]
existing = attributes.instance_state(instance)
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py
index 9d977b221..9f8721de9 100644
--- a/lib/sqlalchemy/orm/properties.py
+++ b/lib/sqlalchemy/orm/properties.py
@@ -193,6 +193,14 @@ class ColumnProperty(StrategizedProperty):
"parententity": self._parentmapper,
"parentmapper": self._parentmapper})
+ @util.memoized_property
+ def info(self):
+ ce = self.__clause_element__()
+ try:
+ return ce.info
+ except AttributeError:
+ return self.prop.info
+
def __getattr__(self, key):
"""proxy attribute access down to the mapped column.
diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py
index fd0c54d88..9e44e01f7 100644
--- a/lib/sqlalchemy/orm/relationships.py
+++ b/lib/sqlalchemy/orm/relationships.py
@@ -352,7 +352,7 @@ class JoinCondition(object):
return
if "foreign" not in binary.left._annotations and \
- "foreign" not in binary.right._annotations:
+ "foreign" not in binary.right._annotations:
col = is_foreign(binary.left, binary.right)
if col is not None:
if col.compare(binary.left):
@@ -451,12 +451,11 @@ class JoinCondition(object):
def visit_binary(binary):
equated = binary.left.compare(binary.right)
if isinstance(binary.left, expression.ColumnClause) and \
- isinstance(binary.right, expression.ColumnClause):
+ isinstance(binary.right, expression.ColumnClause):
# assume one to many - FKs are "remote"
if fn(binary.left):
binary.left = binary.left._annotate({"remote": True})
- if fn(binary.right) and \
- not equated:
+ if fn(binary.right) and not equated:
binary.right = binary.right._annotate(
{"remote": True})
else:
@@ -507,9 +506,9 @@ class JoinCondition(object):
def proc_left_right(left, right):
if isinstance(left, expression.ColumnClause) and \
- isinstance(right, expression.ColumnClause):
+ isinstance(right, expression.ColumnClause):
if self.child_selectable.c.contains_column(right) and \
- self.parent_selectable.c.contains_column(left):
+ self.parent_selectable.c.contains_column(left):
right = right._annotate({"remote": True})
else:
self._warn_non_column_elements()
@@ -532,8 +531,7 @@ class JoinCondition(object):
not self.parent_local_selectable.c.\
contains_column(element)
or self.child_local_selectable.c.\
- contains_column(element)
- ):
+ contains_column(element)):
return element._annotate({"remote": True})
self.primaryjoin = visitors.replacement_traverse(
self.primaryjoin, {}, repl)
@@ -568,7 +566,7 @@ class JoinCondition(object):
def locals_(elem):
if "remote" not in elem._annotations and \
- elem in local_side:
+ elem in local_side:
return elem._annotate({"local": True})
self.primaryjoin = visitors.replacement_traverse(
self.primaryjoin, {}, locals_
@@ -603,7 +601,7 @@ class JoinCondition(object):
can_sync = bool(self.secondary_synchronize_pairs)
if self.support_sync and can_sync or \
- (not self.support_sync and has_foreign):
+ (not self.support_sync and has_foreign):
return
# from here below is just determining the best error message
@@ -685,8 +683,7 @@ class JoinCondition(object):
"Ensure that only those columns referring "
"to a parent column are marked as foreign, "
"either via the foreign() annotation or "
- "via the foreign_keys argument."
- % self.prop)
+ "via the foreign_keys argument." % self.prop)
elif onetomany_fk:
self.direction = ONETOMANY
elif manytoone_fk:
@@ -716,14 +713,14 @@ class JoinCondition(object):
def visit_binary(binary, left, right):
if "remote" in right._annotations and \
"remote" not in left._annotations and \
- self.can_be_synced_fn(left):
+ self.can_be_synced_fn(left):
lrp.add((left, right))
elif "remote" in left._annotations and \
"remote" not in right._annotations and \
- self.can_be_synced_fn(right):
+ self.can_be_synced_fn(right):
lrp.add((right, left))
if binary.operator is operators.eq and \
- self.can_be_synced_fn(left, right):
+ self.can_be_synced_fn(left, right):
if "foreign" in right._annotations:
collection.append((left, right))
elif "foreign" in left._annotations:
diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py
index cc9dd6ba5..f3b8e271d 100644
--- a/lib/sqlalchemy/orm/util.py
+++ b/lib/sqlalchemy/orm/util.py
@@ -1265,3 +1265,40 @@ def attribute_str(instance, attribute):
def state_attribute_str(state, attribute):
return state_str(state) + "." + attribute
+
+
+def randomize_unitofwork():
+ """Use random-ordering sets within the unit of work in order
+ to detect unit of work sorting issues.
+
+ This is a utility function that can be used to help reproduce
+ inconsistent unit of work sorting issues. For example,
+ if two kinds of objects A and B are being inserted, and
+ B has a foreign key reference to A - the A must be inserted first.
+ However, if there is no relationship between A and B, the unit of work
+ won't know to perform this sorting, and an operation may or may not
+ fail, depending on how the ordering works out. Since Python sets
+ and dictionaries have non-deterministic ordering, such an issue may
+ occur on some runs and not on others, and in practice it tends to
+ have a great dependence on the state of the interpreter. This leads
+ to so-called "heisenbugs" where changing entirely irrelevant aspects
+ of the test program still cause the failure behavior to change.
+
+ By calling ``randomize_unitofwork()`` when a script first runs, the
+ ordering of a key series of sets within the unit of work implementation
+ are randomized, so that the script can be minimized down to the fundamental
+ mapping and operation that's failing, while still reproducing the issue
+ on at least some runs.
+
+ This utility is also available when running the test suite via the
+ ``--reversetop`` flag.
+
+ .. versionadded:: 0.8.1 created a standalone version of the
+ ``--reversetop`` feature.
+
+ """
+ from sqlalchemy.orm import unitofwork, session, mapper, dependency
+ from sqlalchemy.util import topological
+ from sqlalchemy.testing.util import RandomSet
+ topological.set = unitofwork.set = session.set = mapper.set = \
+ dependency.set = RandomSet
diff --git a/lib/sqlalchemy/pool.py b/lib/sqlalchemy/pool.py
index f72082ceb..501b6d2a0 100644
--- a/lib/sqlalchemy/pool.py
+++ b/lib/sqlalchemy/pool.py
@@ -736,8 +736,8 @@ class QueuePool(Pool):
self._overflow = 0 - pool_size
self._max_overflow = max_overflow
self._timeout = timeout
- self._overflow_lock = self._max_overflow > -1 and \
- threading.Lock() or DummyLock()
+ self._overflow_lock = threading.Lock() if self._max_overflow > -1 \
+ else DummyLock()
def _do_return_conn(self, conn):
try:
diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py
index 9d14bd3ca..9a07b9de4 100644
--- a/lib/sqlalchemy/schema.py
+++ b/lib/sqlalchemy/schema.py
@@ -93,14 +93,13 @@ def _get_table_key(name, schema):
def _validate_dialect_kwargs(kwargs, name):
# validate remaining kwargs that they all specify DB prefixes
- if len([k for k in kwargs
- if not re.match(
- r'^(?:%s)_' %
- '|'.join(dialects.__all__), k
- )
- ]):
- raise TypeError(
- "Invalid argument(s) for %s: %r" % (name, kwargs.keys()))
+
+ for k in kwargs:
+ m = re.match('^(.+?)_.*', k)
+ if m is None:
+ raise TypeError("Additional arguments should be "
+ "named <dialectname>_<argument>, got '%s'" % k)
+
inspection._self_inspects(SchemaItem)
@@ -2025,7 +2024,7 @@ class ColumnCollectionMixin(object):
for c in columns]
if self._pending_colargs and \
isinstance(self._pending_colargs[0], Column) and \
- self._pending_colargs[0].table is not None:
+ isinstance(self._pending_colargs[0].table, Table):
self._set_parent_with_dispatch(self._pending_colargs[0].table)
def _set_parent(self, table):
@@ -2121,7 +2120,7 @@ class CheckConstraint(Constraint):
elif _autoattach:
cols = sqlutil.find_columns(self.sqltext)
tables = set([c.table for c in cols
- if c.table is not None])
+ if isinstance(c.table, Table)])
if len(tables) == 1:
self._set_parent_with_dispatch(
tables.pop())
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index 59e46de12..5a3a92a3e 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -423,7 +423,7 @@ class SQLCompiler(engine.Compiled):
name = orig_name = column.name
if name is None:
raise exc.CompileError("Cannot compile Column object until "
- "it's 'name' is assigned.")
+ "its 'name' is assigned.")
is_literal = column.is_literal
if not is_literal and isinstance(name, sql._truncated_label):
@@ -787,14 +787,14 @@ class SQLCompiler(engine.Compiled):
existing = self.binds[name]
if existing is not bindparam:
if (existing.unique or bindparam.unique) and \
- not existing.proxy_set.intersection(bindparam.proxy_set):
+ not existing.proxy_set.intersection(
+ bindparam.proxy_set):
raise exc.CompileError(
"Bind parameter '%s' conflicts with "
"unique bind parameter of the same name" %
bindparam.key
)
- elif getattr(existing, '_is_crud', False) or \
- getattr(bindparam, '_is_crud', False):
+ elif existing._is_crud or bindparam._is_crud:
raise exc.CompileError(
"bindparam() name '%s' is reserved "
"for automatic usage in the VALUES or SET "
@@ -1086,14 +1086,9 @@ class SQLCompiler(engine.Compiled):
positional_names=None, **kwargs):
entry = self.stack and self.stack[-1] or {}
- if not asfrom:
- existingfroms = entry.get('from', None)
- else:
- # don't render correlations if we're rendering a FROM list
- # entry
- existingfroms = []
+ existingfroms = entry.get('from', None)
- froms = select._get_display_froms(existingfroms)
+ froms = select._get_display_froms(existingfroms, asfrom=asfrom)
correlate_froms = set(sql._from_objects(*froms))
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index 490004e39..5cef778bb 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -181,10 +181,10 @@ def select(columns=None, whereclause=None, from_obj=[], **kwargs):
string arguments, which will be converted as appropriate into
either :func:`text()` or :func:`literal_column()` constructs.
- See also:
+ .. seealso::
- :ref:`coretutorial_selecting` - Core Tutorial description of
- :func:`.select`.
+ :ref:`coretutorial_selecting` - Core Tutorial description of
+ :func:`.select`.
:param columns:
A list of :class:`.ClauseElement` objects, typically
@@ -464,7 +464,7 @@ def update(table, whereclause=None, values=None, inline=False, **kwargs):
as_scalar()
)
- See also:
+ .. seealso::
:ref:`inserts_and_updates` - SQL Expression
Language Tutorial
@@ -493,7 +493,7 @@ def delete(table, whereclause=None, **kwargs):
condition of the ``UPDATE`` statement. Note that the
:meth:`~Delete.where()` generative method may be used instead.
- See also:
+ .. seealso::
:ref:`deletes` - SQL Expression Tutorial
@@ -2873,6 +2873,8 @@ class BindParameter(ColumnElement):
__visit_name__ = 'bindparam'
quote = None
+ _is_crud = False
+
def __init__(self, key, value, type_=None, unique=False,
callable_=None,
isoutparam=False, required=False,
@@ -3073,7 +3075,7 @@ class Executable(Generative):
See :meth:`.Connection.execution_options` for a full list of
possible options.
- See also:
+ .. seealso::
:meth:`.Connection.execution_options()`
@@ -3444,15 +3446,15 @@ class Case(ColumnElement):
class FunctionElement(Executable, ColumnElement, FromClause):
"""Base for SQL function-oriented constructs.
- See also:
+ .. seealso::
- :class:`.Function` - named SQL function.
+ :class:`.Function` - named SQL function.
- :data:`.func` - namespace which produces registered or ad-hoc
- :class:`.Function` instances.
+ :data:`.func` - namespace which produces registered or ad-hoc
+ :class:`.Function` instances.
- :class:`.GenericFunction` - allows creation of registered function
- types.
+ :class:`.GenericFunction` - allows creation of registered function
+ types.
"""
@@ -3571,15 +3573,13 @@ class Function(FunctionElement):
See the superclass :class:`.FunctionElement` for a description
of public methods.
- See also:
-
- See also:
+ .. seealso::
- :data:`.func` - namespace which produces registered or ad-hoc
- :class:`.Function` instances.
+ :data:`.func` - namespace which produces registered or ad-hoc
+ :class:`.Function` instances.
- :class:`.GenericFunction` - allows creation of registered function
- types.
+ :class:`.GenericFunction` - allows creation of registered function
+ types.
"""
@@ -4725,7 +4725,9 @@ class SelectBase(Executable, FromClause):
"""return a 'scalar' representation of this selectable, embedded as a
subquery with a label.
- See also :meth:`~.SelectBase.as_scalar`.
+ .. seealso::
+
+ :meth:`~.SelectBase.as_scalar`.
"""
return self.as_scalar().label(name)
@@ -4843,9 +4845,9 @@ class SelectBase(Executable, FromClause):
result = conn.execute(statement).fetchall()
- See also:
+ .. seealso::
- :meth:`.orm.query.Query.cte` - ORM version of :meth:`.SelectBase.cte`.
+ :meth:`.orm.query.Query.cte` - ORM version of :meth:`.SelectBase.cte`.
"""
return CTE(self, name=name, recursive=recursive)
@@ -4914,6 +4916,10 @@ class SelectBase(Executable, FromClause):
The criterion will be appended to any pre-existing ORDER BY criterion.
+ This is an **in-place** mutation method; the
+ :meth:`~.SelectBase.order_by` method is preferred, as it provides standard
+ :term:`method chaining`.
+
"""
if len(clauses) == 1 and clauses[0] is None:
self._order_by_clause = ClauseList()
@@ -4927,6 +4933,10 @@ class SelectBase(Executable, FromClause):
The criterion will be appended to any pre-existing GROUP BY criterion.
+ This is an **in-place** mutation method; the
+ :meth:`~.SelectBase.group_by` method is preferred, as it provides standard
+ :term:`method chaining`.
+
"""
if len(clauses) == 1 and clauses[0] is None:
self._group_by_clause = ClauseList()
@@ -4980,7 +4990,7 @@ class CompoundSelect(SelectBase):
INTERSECT_ALL = util.symbol('INTERSECT ALL')
def __init__(self, keyword, *selects, **kwargs):
- self._should_correlate = kwargs.pop('correlate', False)
+ self._auto_correlate = kwargs.pop('correlate', False)
self.keyword = keyword
self.selects = []
@@ -5120,13 +5130,13 @@ class HasPrefixes(object):
class Select(HasPrefixes, SelectBase):
"""Represents a ``SELECT`` statement.
- See also:
+ .. seealso::
- :func:`~.expression.select` - the function which creates
- a :class:`.Select` object.
+ :func:`~.expression.select` - the function which creates
+ a :class:`.Select` object.
- :ref:`coretutorial_selecting` - Core Tutorial description
- of :func:`.select`.
+ :ref:`coretutorial_selecting` - Core Tutorial description
+ of :func:`.select`.
"""
@@ -5159,7 +5169,7 @@ class Select(HasPrefixes, SelectBase):
:class:`SelectBase` superclass.
"""
- self._should_correlate = correlate
+ self._auto_correlate = correlate
if distinct is not False:
if distinct is True:
self._distinct = True
@@ -5232,7 +5242,7 @@ class Select(HasPrefixes, SelectBase):
return froms
- def _get_display_froms(self, existing_froms=None):
+ def _get_display_froms(self, existing_froms=None, asfrom=False):
"""Return the full list of 'from' clauses to be displayed.
Takes into account a set of existing froms which may be
@@ -5258,18 +5268,29 @@ class Select(HasPrefixes, SelectBase):
# using a list to maintain ordering
froms = [f for f in froms if f not in toremove]
- if len(froms) > 1 or self._correlate or self._correlate_except:
+ if not asfrom:
if self._correlate:
- froms = [f for f in froms if f not in
- _cloned_intersection(froms,
- self._correlate)]
+ froms = [
+ f for f in froms if f not in
+ _cloned_intersection(
+ _cloned_intersection(froms, existing_froms or ()),
+ self._correlate
+ )
+ ]
if self._correlate_except:
- froms = [f for f in froms if f in _cloned_intersection(froms,
- self._correlate_except)]
- if self._should_correlate and existing_froms:
- froms = [f for f in froms if f not in
- _cloned_intersection(froms,
- existing_froms)]
+ froms = [
+ f for f in froms if f in
+ _cloned_intersection(
+ froms,
+ self._correlate_except
+ )
+ ]
+
+ if self._auto_correlate and existing_froms and len(froms) > 1:
+ froms = [
+ f for f in froms if f not in
+ _cloned_intersection(froms, existing_froms)
+ ]
if not len(froms):
raise exc.InvalidRequestError("Select statement '%s"
@@ -5642,7 +5663,7 @@ class Select(HasPrefixes, SelectBase):
:ref:`correlated_subqueries`
"""
- self._should_correlate = False
+ self._auto_correlate = False
if fromclauses and fromclauses[0] is None:
self._correlate = ()
else:
@@ -5662,7 +5683,7 @@ class Select(HasPrefixes, SelectBase):
:ref:`correlated_subqueries`
"""
- self._should_correlate = False
+ self._auto_correlate = False
if fromclauses and fromclauses[0] is None:
self._correlate_except = ()
else:
@@ -5671,9 +5692,15 @@ class Select(HasPrefixes, SelectBase):
def append_correlation(self, fromclause):
"""append the given correlation expression to this select()
- construct."""
+ construct.
+
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.correlate` method is preferred, as it provides standard
+ :term:`method chaining`.
+
+ """
- self._should_correlate = False
+ self._auto_correlate = False
self._correlate = set(self._correlate).union(
_interpret_as_from(f) for f in fromclause)
@@ -5681,6 +5708,10 @@ class Select(HasPrefixes, SelectBase):
"""append the given column expression to the columns clause of this
select() construct.
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.column` method is preferred, as it provides standard
+ :term:`method chaining`.
+
"""
self._reset_exported()
column = _interpret_as_column_or_from(column)
@@ -5694,6 +5725,10 @@ class Select(HasPrefixes, SelectBase):
"""append the given columns clause prefix expression to this select()
construct.
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.prefix_with` method is preferred, as it provides standard
+ :term:`method chaining`.
+
"""
clause = _literal_as_text(clause)
self._prefixes = self._prefixes + (clause,)
@@ -5704,6 +5739,10 @@ class Select(HasPrefixes, SelectBase):
The expression will be joined to existing WHERE criterion via AND.
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.where` method is preferred, as it provides standard
+ :term:`method chaining`.
+
"""
self._reset_exported()
whereclause = _literal_as_text(whereclause)
@@ -5719,6 +5758,10 @@ class Select(HasPrefixes, SelectBase):
The expression will be joined to existing HAVING criterion via AND.
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.having` method is preferred, as it provides standard
+ :term:`method chaining`.
+
"""
if self._having is not None:
self._having = and_(self._having, _literal_as_text(having))
@@ -5729,6 +5772,10 @@ class Select(HasPrefixes, SelectBase):
"""append the given FromClause expression to this select() construct's
FROM clause.
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.select_from` method is preferred, as it provides standard
+ :term:`method chaining`.
+
"""
self._reset_exported()
fromclause = _interpret_as_from(fromclause)
@@ -6124,9 +6171,9 @@ class Insert(ValuesBase):
The :class:`.Insert` object is created using the
:func:`~.expression.insert()` function.
- See also:
+ .. seealso::
- :ref:`coretutorial_insert_expressions`
+ :ref:`coretutorial_insert_expressions`
"""
__visit_name__ = 'insert'
diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py
index fd138cfec..520c90f99 100644
--- a/lib/sqlalchemy/sql/util.py
+++ b/lib/sqlalchemy/sql/util.py
@@ -13,12 +13,14 @@ from collections import deque
"""Utility functions that build upon SQL and Schema constructs."""
-def sort_tables(tables, skip_fn=None):
+def sort_tables(tables, skip_fn=None, extra_dependencies=None):
"""sort a collection of Table objects in order of
their foreign-key dependency."""
tables = list(tables)
tuples = []
+ if extra_dependencies is not None:
+ tuples.extend(extra_dependencies)
def visit_foreign_key(fkey):
if fkey.use_alter:
@@ -507,6 +509,9 @@ class AnnotatedColumnElement(Annotated):
"""pull 'key' from parent, if not present"""
return self._Annotated__element.key
+ @util.memoized_property
+ def info(self):
+ return self._Annotated__element.info
# hard-generate Annotated subclasses. this technique
# is used instead of on-the-fly types (i.e. type.__new__())
diff --git a/lib/sqlalchemy/testing/assertsql.py b/lib/sqlalchemy/testing/assertsql.py
index 864ce5b4d..0e250f356 100644
--- a/lib/sqlalchemy/testing/assertsql.py
+++ b/lib/sqlalchemy/testing/assertsql.py
@@ -174,6 +174,8 @@ class CompiledSQL(SQLMatchRule):
params = self.params
if not isinstance(params, list):
params = [params]
+ else:
+ params = list(params)
all_params = list(params)
all_received = list(_received_parameters)
while params:
diff --git a/lib/sqlalchemy/testing/plugin/noseplugin.py b/lib/sqlalchemy/testing/plugin/noseplugin.py
index 4ce76363e..5bd7ff3cd 100644
--- a/lib/sqlalchemy/testing/plugin/noseplugin.py
+++ b/lib/sqlalchemy/testing/plugin/noseplugin.py
@@ -215,11 +215,8 @@ def _set_table_options(options, file_config):
@post
def _reverse_topological(options, file_config):
if options.reversetop:
- from sqlalchemy.orm import unitofwork, session, mapper, dependency
- from sqlalchemy.util import topological
- from sqlalchemy.testing.util import RandomSet
- topological.set = unitofwork.set = session.set = mapper.set = \
- dependency.set = RandomSet
+ from sqlalchemy.orm.util import randomize_unitofwork
+ randomize_unitofwork()
def _requirements_opt(options, opt_str, value, parser):
@@ -361,7 +358,6 @@ class NoseSQLAlchemy(Plugin):
The class being examined by the selector
"""
-
if not issubclass(cls, fixtures.TestBase):
return False
elif cls.__name__.startswith('_'):
diff --git a/lib/sqlalchemy/testing/profiling.py b/lib/sqlalchemy/testing/profiling.py
index ae9d176b7..19a9731be 100644
--- a/lib/sqlalchemy/testing/profiling.py
+++ b/lib/sqlalchemy/testing/profiling.py
@@ -14,11 +14,12 @@ import pstats
import time
import collections
from .. import util
+
try:
import cProfile
except ImportError:
cProfile = None
-from ..util.compat import jython, pypy, win32
+from ..util import jython, pypy, win32, update_wrapper
_current_test = None
@@ -210,7 +211,6 @@ class ProfileStatsFile(object):
profile_f.write("%s %s %s\n" % (test_key, platform_key, c))
profile_f.close()
-from sqlalchemy.util.compat import update_wrapper
def function_call_count(variance=0.05):
diff --git a/lib/sqlalchemy/testing/runner.py b/lib/sqlalchemy/testing/runner.py
index 6ec73d7c8..2bdbaebd1 100644
--- a/lib/sqlalchemy/testing/runner.py
+++ b/lib/sqlalchemy/testing/runner.py
@@ -31,3 +31,13 @@ import nose
def main():
nose.main(addplugins=[NoseSQLAlchemy()])
+
+def setup_py_test():
+ """Runner to use for the 'test_suite' entry of your setup.py.
+
+ Prevents any name clash shenanigans from the command line
+ argument "test" that the "setup.py test" command sends
+ to nose.
+
+ """
+ nose.main(addplugins=[NoseSQLAlchemy()], argv=['runner'])
diff --git a/lib/sqlalchemy/testing/schema.py b/lib/sqlalchemy/testing/schema.py
index ad233ec22..325d74f1e 100644
--- a/lib/sqlalchemy/testing/schema.py
+++ b/lib/sqlalchemy/testing/schema.py
@@ -66,18 +66,27 @@ def Column(*args, **kw):
col = schema.Column(*args, **kw)
if 'test_needs_autoincrement' in test_opts and \
- kw.get('primary_key', False) and \
- exclusions.against('firebird', 'oracle'):
- def add_seq(c, tbl):
- c._init_items(
- schema.Sequence(_truncate_name(
- config.db.dialect, tbl.name + '_' + c.name + '_seq'),
- optional=True)
- )
- event.listen(col, 'after_parent_attach', add_seq, propagate=True)
+ kw.get('primary_key', False):
+
+ # allow any test suite to pick up on this
+ col.info['test_needs_autoincrement'] = True
+
+ # hardcoded rule for firebird, oracle; this should
+ # be moved out
+ if exclusions.against('firebird', 'oracle'):
+ def add_seq(c, tbl):
+ c._init_items(
+ schema.Sequence(_truncate_name(
+ config.db.dialect, tbl.name + '_' + c.name + '_seq'),
+ optional=True)
+ )
+ event.listen(col, 'after_parent_attach', add_seq, propagate=True)
return col
+
+
+
def _truncate_name(dialect, name):
if len(name) > dialect.max_identifier_length:
return name[0:max(dialect.max_identifier_length - 6, 0)] + \
diff --git a/lib/sqlalchemy/testing/suite/test_insert.py b/lib/sqlalchemy/testing/suite/test_insert.py
index 66aa1ecfa..a00fde312 100644
--- a/lib/sqlalchemy/testing/suite/test_insert.py
+++ b/lib/sqlalchemy/testing/suite/test_insert.py
@@ -33,7 +33,7 @@ class LastrowidTest(fixtures.TablesTest):
row = conn.execute(table.select()).first()
eq_(
row,
- (1, "some data")
+ (config.db.dialect.default_sequence_base, "some data")
)
def test_autoincrement_on_insert(self):
@@ -132,7 +132,7 @@ class ReturningTest(fixtures.TablesTest):
row = conn.execute(table.select()).first()
eq_(
row,
- (1, "some data")
+ (config.db.dialect.default_sequence_base, "some data")
)
@classmethod
diff --git a/lib/sqlalchemy/testing/suite/test_reflection.py b/lib/sqlalchemy/testing/suite/test_reflection.py
index 5beed6aad..7cae48572 100644
--- a/lib/sqlalchemy/testing/suite/test_reflection.py
+++ b/lib/sqlalchemy/testing/suite/test_reflection.py
@@ -87,8 +87,10 @@ class ComponentReflectionTest(fixtures.TablesTest):
test_needs_fk=True,
)
- cls.define_index(metadata, users)
- cls.define_views(metadata, schema)
+ if testing.requires.index_reflection.enabled:
+ cls.define_index(metadata, users)
+ if testing.requires.view_reflection.enabled:
+ cls.define_views(metadata, schema)
@classmethod
def define_index(cls, metadata, users):
@@ -121,12 +123,14 @@ class ComponentReflectionTest(fixtures.TablesTest):
self.assert_('test_schema' in insp.get_schema_names())
+ @testing.requires.schema_reflection
def test_dialect_initialize(self):
engine = engines.testing_engine()
assert not hasattr(engine.dialect, 'default_schema_name')
inspect(engine)
assert hasattr(engine.dialect, 'default_schema_name')
+ @testing.requires.schema_reflection
def test_get_default_schema_name(self):
insp = inspect(testing.db)
eq_(insp.default_schema_name, testing.db.dialect.default_schema_name)
@@ -157,6 +161,7 @@ class ComponentReflectionTest(fixtures.TablesTest):
self._test_get_table_names()
@testing.requires.table_reflection
+ @testing.requires.foreign_key_constraint_reflection
def test_get_table_names_fks(self):
self._test_get_table_names(order_by='foreign_key')
@@ -261,6 +266,7 @@ class ComponentReflectionTest(fixtures.TablesTest):
self._test_get_pk_constraint()
@testing.requires.table_reflection
+ @testing.requires.primary_key_constraint_reflection
@testing.requires.schemas
def test_get_pk_constraint_with_schema(self):
self._test_get_pk_constraint(schema='test_schema')
diff --git a/lib/sqlalchemy/testing/util.py b/lib/sqlalchemy/testing/util.py
index 2592c341e..d9ff14eaf 100644
--- a/lib/sqlalchemy/testing/util.py
+++ b/lib/sqlalchemy/testing/util.py
@@ -1,6 +1,5 @@
from ..util import jython, pypy, defaultdict, decorator
-from ..util.compat import decimal
-
+import decimal
import gc
import time
import random
diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py
index b9f7b9444..08aba4b56 100644
--- a/lib/sqlalchemy/types.py
+++ b/lib/sqlalchemy/types.py
@@ -25,10 +25,10 @@ import codecs
from . import exc, schema, util, processors, events, event
from .sql import operators
-from .sql.expression import _DefaultColumnComparator, column, bindparam
+from .sql.expression import _DefaultColumnComparator
from .util import pickle
-from .util.compat import decimal
from .sql.visitors import Visitable
+import decimal
default = util.importlater("sqlalchemy.engine", "default")
NoneType = type(None)
@@ -1372,8 +1372,7 @@ class Numeric(_DateAffinity, TypeEngine):
implementations however, most of which contain an import for plain
``decimal`` in their source code, even though some such as psycopg2
provide hooks for alternate adapters. SQLAlchemy imports ``decimal``
- globally as well. While the alternate ``Decimal`` class can be patched
- into SQLA's ``decimal`` module, overall the most straightforward and
+ globally as well. The most straightforward and
foolproof way to use "cdecimal" given current DBAPI and Python support
is to patch it directly into sys.modules before anything else is
imported::
diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py
index 249c46ead..57bbdca85 100644
--- a/lib/sqlalchemy/util/__init__.py
+++ b/lib/sqlalchemy/util/__init__.py
@@ -4,10 +4,9 @@
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
-from .compat import callable, cmp, reduce, defaultdict, py25_dict, \
+from .compat import callable, cmp, reduce, \
threading, py3k, py3k_warning, jython, pypy, cpython, win32, set_types, \
- buffer, pickle, update_wrapper, partial, md5_hex, decode_slice, \
- dottedgetter, parse_qsl, any, contextmanager, namedtuple, next, WeakSet
+ pickle, dottedgetter, parse_qsl, namedtuple, next, WeakSet
from ._collections import KeyedTuple, ImmutableContainer, immutabledict, \
Properties, OrderedProperties, ImmutableProperties, OrderedDict, \
@@ -21,8 +20,8 @@ from .langhelpers import iterate_attributes, class_hierarchy, \
portable_instancemethod, unbound_method_to_callable, \
getargspec_init, format_argspec_init, format_argspec_plus, \
get_func_kwargs, get_cls_kwargs, decorator, as_interface, \
- memoized_property, memoized_instancemethod, \
- group_expirable_memoized_property, importlater, \
+ memoized_property, memoized_instancemethod, md5_hex, \
+ group_expirable_memoized_property, importlater, decode_slice, \
monkeypatch_proxied_specials, asbool, bool_or_str, coerce_kw_type,\
duck_type_collection, assert_arg_type, symbol, dictlike_iteritems,\
classproperty, set_creation_order, warn_exception, warn, NoneType,\
@@ -31,3 +30,10 @@ from .langhelpers import iterate_attributes, class_hierarchy, \
from .deprecations import warn_deprecated, warn_pending_deprecation, \
deprecated, pending_deprecation
+
+# things that used to be not always available,
+# but are now as of current support Python versions
+from collections import defaultdict
+from functools import partial
+from functools import update_wrapper
+from contextlib import contextmanager
diff --git a/lib/sqlalchemy/util/_collections.py b/lib/sqlalchemy/util/_collections.py
index ca77103b2..2c9c982fb 100644
--- a/lib/sqlalchemy/util/_collections.py
+++ b/lib/sqlalchemy/util/_collections.py
@@ -6,7 +6,6 @@
"""Collection classes and helpers."""
-import sys
import itertools
import weakref
import operator
@@ -649,37 +648,25 @@ class OrderedIdentitySet(IdentitySet):
self.add(o)
-if sys.version_info >= (2, 5):
- class PopulateDict(dict):
- """A dict which populates missing values via a creation function.
+class PopulateDict(dict):
+ """A dict which populates missing values via a creation function.
- Note the creation function takes a key, unlike
- collections.defaultdict.
+ Note the creation function takes a key, unlike
+ collections.defaultdict.
- """
-
- def __init__(self, creator):
- self.creator = creator
-
- def __missing__(self, key):
- self[key] = val = self.creator(key)
- return val
-else:
- class PopulateDict(dict):
- """A dict which populates missing values via a creation function."""
+ """
- def __init__(self, creator):
- self.creator = creator
+ def __init__(self, creator):
+ self.creator = creator
- def __getitem__(self, key):
- try:
- return dict.__getitem__(self, key)
- except KeyError:
- self[key] = value = self.creator(key)
- return value
+ def __missing__(self, key):
+ self[key] = val = self.creator(key)
+ return val
-# define collections that are capable of storing
+# Define collections that are capable of storing
# ColumnElement objects as hashable keys/elements.
+# At this point, these are mostly historical, things
+# used to be more complicated.
column_set = set
column_dict = dict
ordered_column_set = OrderedSet
diff --git a/lib/sqlalchemy/util/compat.py b/lib/sqlalchemy/util/compat.py
index 3725a8491..2a0f06f8e 100644
--- a/lib/sqlalchemy/util/compat.py
+++ b/lib/sqlalchemy/util/compat.py
@@ -54,44 +54,6 @@ else:
except ImportError:
import pickle
-
-# a controversial feature, required by MySQLdb currently
-def buffer(x):
- return x
-
-# Py2K
-buffer = buffer
-# end Py2K
-
-try:
- from contextlib import contextmanager
-except ImportError:
- def contextmanager(fn):
- return fn
-
-try:
- from functools import update_wrapper
-except ImportError:
- def update_wrapper(wrapper, wrapped,
- assigned=('__doc__', '__module__', '__name__'),
- updated=('__dict__',)):
- for attr in assigned:
- setattr(wrapper, attr, getattr(wrapped, attr))
- for attr in updated:
- getattr(wrapper, attr).update(getattr(wrapped, attr, ()))
- return wrapper
-
-try:
- from functools import partial
-except ImportError:
- def partial(func, *args, **keywords):
- def newfunc(*fargs, **fkeywords):
- newkeywords = keywords.copy()
- newkeywords.update(fkeywords)
- return func(*(args + fargs), **newkeywords)
- return newfunc
-
-
if sys.version_info < (2, 6):
# emits a nasty deprecation warning
# in newer pythons
@@ -132,52 +94,6 @@ except ImportError:
return tuptype
try:
- from collections import defaultdict
-except ImportError:
- class defaultdict(dict):
-
- def __init__(self, default_factory=None, *a, **kw):
- if (default_factory is not None and
- not hasattr(default_factory, '__call__')):
- raise TypeError('first argument must be callable')
- dict.__init__(self, *a, **kw)
- self.default_factory = default_factory
-
- def __getitem__(self, key):
- try:
- return dict.__getitem__(self, key)
- except KeyError:
- return self.__missing__(key)
-
- def __missing__(self, key):
- if self.default_factory is None:
- raise KeyError(key)
- self[key] = value = self.default_factory()
- return value
-
- def __reduce__(self):
- if self.default_factory is None:
- args = tuple()
- else:
- args = self.default_factory,
- return type(self), args, None, None, self.iteritems()
-
- def copy(self):
- return self.__copy__()
-
- def __copy__(self):
- return type(self)(self.default_factory, self)
-
- def __deepcopy__(self, memo):
- import copy
- return type(self)(self.default_factory,
- copy.deepcopy(self.items()))
-
- def __repr__(self):
- return 'defaultdict(%s, %s)' % (self.default_factory,
- dict.__repr__(self))
-
-try:
from weakref import WeakSet
except:
import weakref
@@ -199,79 +115,12 @@ except:
def add(self, other):
self._storage[other] = True
-
-# find or create a dict implementation that supports __missing__
-class _probe(dict):
- def __missing__(self, key):
- return 1
-
-try:
- try:
- _probe()['missing']
- py25_dict = dict
- except KeyError:
- class py25_dict(dict):
- def __getitem__(self, key):
- try:
- return dict.__getitem__(self, key)
- except KeyError:
- try:
- missing = self.__missing__
- except AttributeError:
- raise KeyError(key)
- else:
- return missing(key)
-finally:
- del _probe
-
-
-try:
- import hashlib
- _md5 = hashlib.md5
-except ImportError:
- import md5
- _md5 = md5.new
-
-
-def md5_hex(x):
- # Py3K
- #x = x.encode('utf-8')
- m = _md5()
- m.update(x)
- return m.hexdigest()
-
import time
if win32 or jython:
time_func = time.clock
else:
time_func = time.time
-if sys.version_info >= (2, 5):
- any = any
-else:
- def any(iterator):
- for item in iterator:
- if bool(item):
- return True
- else:
- return False
-
-if sys.version_info >= (2, 5):
- def decode_slice(slc):
- """decode a slice object as sent to __getitem__.
-
- takes into account the 2.5 __index__() method, basically.
-
- """
- ret = []
- for x in slc.start, slc.stop, slc.step:
- if hasattr(x, '__index__'):
- x = x.__index__()
- ret.append(x)
- return tuple(ret)
-else:
- def decode_slice(slc):
- return (slc.start, slc.stop, slc.step)
if sys.version_info >= (2, 6):
from operator import attrgetter as dottedgetter
@@ -283,5 +132,11 @@ else:
return obj
return g
+# Adapted from six.py
+if py3k:
+ def b(s):
+ return s.encode("latin-1")
+else:
+ def b(s):
+ return s
-import decimal
diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py
index a9b791234..e3aed24d8 100644
--- a/lib/sqlalchemy/util/langhelpers.py
+++ b/lib/sqlalchemy/util/langhelpers.py
@@ -15,10 +15,31 @@ import re
import sys
import types
import warnings
-from .compat import update_wrapper, set_types, threading, \
+from .compat import set_types, threading, \
callable, inspect_getfullargspec
+from functools import update_wrapper
from .. import exc
+import hashlib
+def md5_hex(x):
+ # Py3K
+ #x = x.encode('utf-8')
+ m = hashlib.md5()
+ m.update(x)
+ return m.hexdigest()
+
+def decode_slice(slc):
+ """decode a slice object as sent to __getitem__.
+
+ takes into account the 2.5 __index__() method, basically.
+
+ """
+ ret = []
+ for x in slc.start, slc.stop, slc.step:
+ if hasattr(x, '__index__'):
+ x = x.__index__()
+ ret.append(x)
+ return tuple(ret)
def _unique_symbols(used, *bases):
used = set(used)
@@ -123,7 +144,7 @@ def get_cls_kwargs(cls):
ctr = class_.__dict__.get('__init__', False)
if (not ctr or
not isinstance(ctr, types.FunctionType) or
- not isinstance(ctr.func_code, types.CodeType)):
+ not isinstance(ctr.func_code, types.CodeType)):
stack.update(class_.__bases__)
continue
@@ -256,7 +277,6 @@ def format_argspec_init(method, grouped=True):
try:
return format_argspec_plus(method, grouped=grouped)
except TypeError:
- self_arg = 'self'
if method is object.__init__:
args = grouped and '(self)' or 'self'
else:
@@ -784,7 +804,7 @@ def duck_type_collection(specimen, default=None):
if hasattr(specimen, '__emulates__'):
# canonicalize set vs sets.Set to a standard: the builtin set
if (specimen.__emulates__ is not None and
- issubclass(specimen.__emulates__, set_types)):
+ issubclass(specimen.__emulates__, set_types)):
return set
else:
return specimen.__emulates__
diff --git a/lib/sqlalchemy/util/topological.py b/lib/sqlalchemy/util/topological.py
index 6f895e7b7..de3dfd0ae 100644
--- a/lib/sqlalchemy/util/topological.py
+++ b/lib/sqlalchemy/util/topological.py
@@ -49,7 +49,8 @@ def sort(tuples, allitems):
def find_cycles(tuples, allitems):
- # straight from gvr with some mods
+ # adapted from:
+ # http://neopythonic.blogspot.com/2009/01/detecting-cycles-in-directed-graph.html
edges = util.defaultdict(set)
for parent, child in tuples: