summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/cextension/resultproxy.c8
-rw-r--r--lib/sqlalchemy/dialects/sqlite/base.py16
-rw-r--r--lib/sqlalchemy/engine/base.py8
-rw-r--r--lib/sqlalchemy/orm/attributes.py7
-rw-r--r--lib/sqlalchemy/orm/mapper.py35
-rw-r--r--lib/sqlalchemy/orm/properties.py3
-rw-r--r--lib/sqlalchemy/orm/session.py5
-rw-r--r--lib/sqlalchemy/orm/strategies.py8
-rw-r--r--lib/sqlalchemy/orm/unitofwork.py10
-rw-r--r--lib/sqlalchemy/orm/util.py33
-rw-r--r--lib/sqlalchemy/schema.py19
-rw-r--r--lib/sqlalchemy/sql/compiler.py7
-rw-r--r--lib/sqlalchemy/sql/expression.py41
-rw-r--r--lib/sqlalchemy/test/requires.py22
-rw-r--r--lib/sqlalchemy/test/util.py2
-rw-r--r--lib/sqlalchemy/types.py7
-rw-r--r--lib/sqlalchemy/util.py46
17 files changed, 162 insertions, 115 deletions
diff --git a/lib/sqlalchemy/cextension/resultproxy.c b/lib/sqlalchemy/cextension/resultproxy.c
index 7404b9ed2..73e127345 100644
--- a/lib/sqlalchemy/cextension/resultproxy.c
+++ b/lib/sqlalchemy/cextension/resultproxy.c
@@ -327,6 +327,12 @@ BaseRowProxy_subscript(BaseRowProxy *self, PyObject *key)
}
static PyObject *
+BaseRowProxy_getitem(PyObject *self, Py_ssize_t i)
+{
+ return BaseRowProxy_subscript((BaseRowProxy*)self, PyInt_FromSsize_t(i));
+}
+
+static PyObject *
BaseRowProxy_getattro(BaseRowProxy *self, PyObject *name)
{
PyObject *tmp;
@@ -506,7 +512,7 @@ static PySequenceMethods BaseRowProxy_as_sequence = {
(lenfunc)BaseRowProxy_length, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
- 0, /* sq_item */
+ (ssizeargfunc)BaseRowProxy_getitem, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py
index b84b18e68..994904b6a 100644
--- a/lib/sqlalchemy/dialects/sqlite/base.py
+++ b/lib/sqlalchemy/dialects/sqlite/base.py
@@ -270,7 +270,21 @@ class SQLiteDDLCompiler(compiler.DDLCompiler):
return super(SQLiteDDLCompiler, self).\
visit_primary_key_constraint(constraint)
-
+
+ def visit_foreign_key_constraint(self, constraint):
+
+ local_table = constraint._elements.values()[0].parent.table
+ remote_table = list(constraint._elements.values())[0].column.table
+
+ if local_table.schema != remote_table.schema:
+ return None
+ else:
+ return super(SQLiteDDLCompiler, self).visit_foreign_key_constraint(constraint)
+
+ def define_constraint_remote_table(self, constraint, table, preparer):
+ """Format the remote table clause of a CREATE CONSTRAINT clause."""
+
+ return preparer.format_table(table, use_schema=False)
def visit_create_index(self, create):
index = create.element
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py
index 9d80af7a7..326c33f2d 100644
--- a/lib/sqlalchemy/engine/base.py
+++ b/lib/sqlalchemy/engine/base.py
@@ -2142,6 +2142,14 @@ class RowProxy(BaseRowProxy):
def itervalues(self):
return iter(self)
+try:
+ # Register RowProxy with Sequence,
+ # so sequence protocol is implemented
+ from collections import Sequence
+ Sequence.register(RowProxy)
+except ImportError:
+ pass
+
class ResultMetaData(object):
"""Handle cursor.description, applying additional info from an execution
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py
index fcaabfddd..6872dd645 100644
--- a/lib/sqlalchemy/orm/attributes.py
+++ b/lib/sqlalchemy/orm/attributes.py
@@ -19,10 +19,7 @@ from sqlalchemy import util, event
from sqlalchemy.orm import interfaces, collections, events
import sqlalchemy.exceptions as sa_exc
-# lazy imports
-_entity_info = None
-identity_equal = None
-state = None
+mapperutil = util.importlater("sqlalchemy.orm", "util")
PASSIVE_NO_RESULT = util.symbol('PASSIVE_NO_RESULT')
ATTR_WAS_SET = util.symbol('ATTR_WAS_SET')
@@ -542,7 +539,7 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl):
compare_function=compare_function,
**kwargs)
if compare_function is None:
- self.is_equal = identity_equal
+ self.is_equal = mapperutil.identity_equal
def delete(self, state, dict_):
old = self.get(state, dict_)
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 7fdf21c6c..75ba0b5c0 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -31,6 +31,8 @@ from sqlalchemy.orm.util import _INSTRUMENTOR, _class_to_mapper, \
_state_mapper, class_mapper, instance_str, state_str
import sys
+sessionlib = util.importlater("sqlalchemy.orm", "session")
+properties = util.importlater("sqlalchemy.orm", "properties")
__all__ = (
'Mapper',
@@ -54,13 +56,6 @@ NO_ATTRIBUTE = util.symbol('NO_ATTRIBUTE')
# lock used to synchronize the "mapper compile" step
_COMPILE_MUTEX = util.threading.RLock()
-# initialize these lazily
-ColumnProperty = None
-RelationshipProperty = None
-ConcreteInheritedProperty = None
-_expire_state = None
-_state_session = None
-
class Mapper(object):
"""Define the correlation of class attributes to database table
columns.
@@ -590,7 +585,7 @@ class Mapper(object):
self._configure_property(
col.key,
- ColumnProperty(col, _instrument=instrument),
+ properties.ColumnProperty(col, _instrument=instrument),
init=False, setparent=True)
def _adapt_inherited_property(self, key, prop, init):
@@ -599,7 +594,7 @@ class Mapper(object):
elif key not in self._props:
self._configure_property(
key,
- ConcreteInheritedProperty(),
+ properties.ConcreteInheritedProperty(),
init=init, setparent=True)
def _configure_property(self, key, prop, init=True, setparent=True):
@@ -607,7 +602,7 @@ class Mapper(object):
if not isinstance(prop, MapperProperty):
# we were passed a Column or a list of Columns;
- # generate a ColumnProperty
+ # generate a properties.ColumnProperty
columns = util.to_list(prop)
column = columns[0]
if not expression.is_column(column):
@@ -617,12 +612,12 @@ class Mapper(object):
prop = self._props.get(key, None)
- if isinstance(prop, ColumnProperty):
+ if isinstance(prop, properties.ColumnProperty):
# 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.
+ # existing properties.ColumnProperty from an inheriting mapper.
# make a copy and append our column to it
prop = prop.copy()
else:
@@ -637,9 +632,9 @@ class Mapper(object):
# this hypothetically changes to
# prop.columns.insert(0, column) when we do [ticket:1892]
prop.columns.append(column)
- self._log("appending to existing ColumnProperty %s" % (key))
+ self._log("appending to existing properties.ColumnProperty %s" % (key))
- elif prop is None or isinstance(prop, ConcreteInheritedProperty):
+ elif prop is None or isinstance(prop, properties.ConcreteInheritedProperty):
mapped_column = []
for c in columns:
mc = self.mapped_table.corresponding_column(c)
@@ -660,7 +655,7 @@ class Mapper(object):
"force this column to be mapped as a read-only "
"attribute." % (key, self, c))
mapped_column.append(mc)
- prop = ColumnProperty(*mapped_column)
+ prop = properties.ColumnProperty(*mapped_column)
else:
raise sa_exc.ArgumentError(
"WARNING: when configuring property '%s' on %s, "
@@ -674,7 +669,7 @@ class Mapper(object):
"columns get mapped." %
(key, self, column.key, prop))
- if isinstance(prop, ColumnProperty):
+ if isinstance(prop, properties.ColumnProperty):
col = self.mapped_table.corresponding_column(prop.columns[0])
# if the column is not present in the mapped table,
@@ -713,7 +708,7 @@ class Mapper(object):
col not in self._cols_by_table[col.table]:
self._cols_by_table[col.table].add(col)
- # if this ColumnProperty represents the "polymorphic
+ # if this properties.ColumnProperty represents the "polymorphic
# discriminator" column, mark it. We'll need this when rendering
# columns in SELECT statements.
if not hasattr(prop, '_is_polymorphic_discriminator'):
@@ -1887,7 +1882,7 @@ class Mapper(object):
)
if readonly:
- _expire_state(state, state.dict, readonly)
+ sessionlib._expire_state(state, state.dict, readonly)
# if eager_defaults option is enabled,
# refresh whatever has been expired.
@@ -1925,7 +1920,7 @@ class Mapper(object):
self._set_state_attr_by_column(state, dict_, c, params[c.key])
if postfetch_cols:
- _expire_state(state, state.dict,
+ sessionlib._expire_state(state, state.dict,
[self._columntoproperty[c].key
for c in postfetch_cols]
)
@@ -2430,7 +2425,7 @@ def _load_scalar_attributes(state, attribute_names):
"""initiate a column-based attribute refresh operation."""
mapper = _state_mapper(state)
- session = _state_session(state)
+ session = sessionlib._state_session(state)
if not session:
raise orm_exc.DetachedInstanceError(
"Instance %s is not bound to a Session; "
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py
index 0cbbf630d..feee041ce 100644
--- a/lib/sqlalchemy/orm/properties.py
+++ b/lib/sqlalchemy/orm/properties.py
@@ -1483,6 +1483,3 @@ class RelationshipProperty(StrategizedProperty):
PropertyLoader = RelationProperty = RelationshipProperty
log.class_logger(RelationshipProperty)
-mapper.ColumnProperty = ColumnProperty
-mapper.RelationshipProperty = RelationshipProperty
-mapper.ConcreteInheritedProperty = ConcreteInheritedProperty
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index 710d3213a..c384cfc3d 100644
--- a/lib/sqlalchemy/orm/session.py
+++ b/lib/sqlalchemy/orm/session.py
@@ -1695,8 +1695,3 @@ def _state_session(state):
pass
return None
-# Lazy initialization to avoid circular imports
-unitofwork._state_session = _state_session
-from sqlalchemy.orm import mapper
-mapper._expire_state = _expire_state
-mapper._state_session = _state_session
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index 1de2dc3ee..17f165b64 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -741,14 +741,16 @@ class SubqueryLoader(AbstractRelationshipLoader):
("orig_query", SubqueryLoader),
context.query)
+ subq_mapper = mapperutil._class_to_mapper(subq_path[0])
+
# determine attributes of the leftmost mapper
- if self.parent.isa(subq_path[0]) and self.key==subq_path[1]:
+ if self.parent.isa(subq_mapper) and self.key==subq_path[1]:
leftmost_mapper, leftmost_prop = \
self.parent, self.parent_property
else:
leftmost_mapper, leftmost_prop = \
- subq_path[0], \
- subq_path[0].get_property(subq_path[1])
+ subq_mapper, \
+ subq_mapper.get_property(subq_path[1])
leftmost_cols, remote_cols = self._local_remote_columns(leftmost_prop)
leftmost_attr = [
diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py
index a9808e6ba..673591e8e 100644
--- a/lib/sqlalchemy/orm/unitofwork.py
+++ b/lib/sqlalchemy/orm/unitofwork.py
@@ -16,9 +16,7 @@ from sqlalchemy import util, topological
from sqlalchemy.orm import attributes, interfaces
from sqlalchemy.orm import util as mapperutil
from sqlalchemy.orm.util import _state_mapper
-
-# Load lazily
-_state_session = None
+session = util.importlater("sqlalchemy.orm", "session")
class UOWEventHandler(interfaces.AttributeExtension):
"""An event handler added to all relationship attributes which handles
@@ -34,7 +32,7 @@ class UOWEventHandler(interfaces.AttributeExtension):
# process "save_update" cascade rules for when
# an instance is appended to the list of another instance
- sess = _state_session(state)
+ sess = session._state_session(state)
if sess:
prop = _state_mapper(state).get_property(self.key)
if prop.cascade.save_update and \
@@ -44,7 +42,7 @@ class UOWEventHandler(interfaces.AttributeExtension):
return item
def remove(self, state, item, initiator):
- sess = _state_session(state)
+ sess = session._state_session(state)
if sess:
prop = _state_mapper(state).get_property(self.key)
# expunge pending orphans
@@ -59,7 +57,7 @@ class UOWEventHandler(interfaces.AttributeExtension):
if oldvalue is newvalue:
return newvalue
- sess = _state_session(state)
+ sess = session._state_session(state)
if sess:
prop = _state_mapper(state).get_property(self.key)
if newvalue is not None and \
diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py
index c2b79666c..9447eed30 100644
--- a/lib/sqlalchemy/orm/util.py
+++ b/lib/sqlalchemy/orm/util.py
@@ -13,7 +13,7 @@ from sqlalchemy.orm.interfaces import MapperExtension, EXT_CONTINUE,\
AttributeExtension
from sqlalchemy.orm import attributes, exc
-mapperlib = None
+mapperlib = util.importlater("sqlalchemy.orm", "mapperlib")
all_cascades = frozenset(("delete", "delete-orphan", "all", "merge",
"expunge", "save-update", "refresh-expire",
@@ -460,10 +460,6 @@ def _entity_info(entity, compile=True):
if isinstance(entity, AliasedClass):
return entity._AliasedClass__mapper, entity._AliasedClass__alias, True
- global mapperlib
- if mapperlib is None:
- from sqlalchemy.orm import mapperlib
-
if isinstance(entity, mapperlib.Mapper):
mapper = entity
@@ -558,24 +554,28 @@ def class_mapper(class_, compile=True):
def _class_to_mapper(class_or_mapper, compile=True):
if _is_aliased_class(class_or_mapper):
return class_or_mapper._AliasedClass__mapper
+
elif isinstance(class_or_mapper, type):
- return class_mapper(class_or_mapper, compile=compile)
- elif hasattr(class_or_mapper, 'compile'):
- if compile:
- return class_or_mapper.compile()
- else:
- return class_or_mapper
+ try:
+ class_manager = attributes.manager_of_class(class_or_mapper)
+ mapper = class_manager.mapper
+ except exc.NO_STATE:
+ raise exc.UnmappedClassError(class_or_mapper)
+ elif isinstance(class_or_mapper, mapperlib.Mapper):
+ mapper = class_or_mapper
else:
raise exc.UnmappedClassError(class_or_mapper)
+
+ if compile:
+ return mapper.compile()
+ else:
+ return mapper
def has_identity(object):
state = attributes.instance_state(object)
return state.has_identity
def _is_mapped_class(cls):
- global mapperlib
- if mapperlib is None:
- from sqlalchemy.orm import mapperlib
if isinstance(cls, (AliasedClass, mapperlib.Mapper)):
return True
if isinstance(cls, expression.ClauseElement):
@@ -618,8 +618,3 @@ def identity_equal(a, b):
return False
return state_a.key == state_b.key
-
-# TODO: Avoid circular import.
-attributes.identity_equal = identity_equal
-attributes._is_aliased_class = _is_aliased_class
-attributes._entity_info = _entity_info
diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py
index 15b58e953..607b55d3f 100644
--- a/lib/sqlalchemy/schema.py
+++ b/lib/sqlalchemy/schema.py
@@ -33,7 +33,9 @@ from sqlalchemy import exc, util, dialects
from sqlalchemy.sql import expression, visitors
from sqlalchemy import event, events
-URL = None
+sqlutil = util.importlater("sqlalchemy.sql", "util")
+url = util.importlater("sqlalchemy.engine", "url")
+
__all__ = ['SchemaItem', 'Table', 'Column', 'ForeignKey', 'Sequence', 'Index',
'ForeignKeyConstraint', 'PrimaryKeyConstraint', 'CheckConstraint',
@@ -1957,11 +1959,7 @@ class MetaData(SchemaItem):
def _bind_to(self, bind):
"""Bind this MetaData to an Engine, Connection, string or URL."""
- global URL
- if URL is None:
- from sqlalchemy.engine.url import URL
-
- if isinstance(bind, (basestring, URL)):
+ if isinstance(bind, (basestring, url.URL)):
from sqlalchemy import create_engine
self._bind = create_engine(bind)
else:
@@ -1985,8 +1983,7 @@ class MetaData(SchemaItem):
"""Returns a list of ``Table`` objects sorted in order of
dependency.
"""
- from sqlalchemy.sql.util import sort_tables
- return sort_tables(self.tables.itervalues())
+ return sqlutil.sort_tables(self.tables.itervalues())
def reflect(self, bind=None, schema=None, views=False, only=None):
"""Load all available table definitions from the database.
@@ -2156,11 +2153,7 @@ class ThreadLocalMetaData(MetaData):
def _bind_to(self, bind):
"""Bind to a Connectable in the caller's thread."""
- global URL
- if URL is None:
- from sqlalchemy.engine.url import URL
-
- if isinstance(bind, (basestring, URL)):
+ if isinstance(bind, (basestring, url.URL)):
try:
self.context._engine = self.__engines[bind]
except KeyError:
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index d3b8bf023..4b41c6ed3 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -1306,7 +1306,7 @@ class DDLCompiler(engine.Compiled):
text += "FOREIGN KEY(%s) REFERENCES %s (%s)" % (
', '.join(preparer.quote(f.parent.name, f.parent.quote)
for f in constraint._elements.values()),
- preparer.format_table(remote_table),
+ self.define_constraint_remote_table(constraint, remote_table, preparer),
', '.join(preparer.quote(f.column.name, f.column.quote)
for f in constraint._elements.values())
)
@@ -1314,6 +1314,11 @@ class DDLCompiler(engine.Compiled):
text += self.define_constraint_deferrability(constraint)
return text
+ def define_constraint_remote_table(self, constraint, table, preparer):
+ """Format the remote table clause of a CREATE CONSTRAINT clause."""
+
+ return preparer.format_table(table)
+
def visit_unique_constraint(self, constraint):
text = ""
if constraint.name is not None:
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index 625893a68..c3dc339a5 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -29,13 +29,15 @@ to stay the same in future releases.
import itertools, re
from operator import attrgetter
-from sqlalchemy import util, exc #, types as sqltypes
+from sqlalchemy import util, exc
from sqlalchemy.sql import operators
from sqlalchemy.sql.visitors import Visitable, cloned_traverse
import operator
-functions, sql_util, sqltypes = None, None, None
-DefaultDialect = None
+functions = util.importlater("sqlalchemy.sql", "functions")
+sqlutil = util.importlater("sqlalchemy.sql", "util")
+sqltypes = util.importlater("sqlalchemy", "types")
+default = util.importlater("sqlalchemy.engine", "default")
__all__ = [
'Alias', 'ClauseElement', 'ColumnCollection', 'ColumnElement',
@@ -957,9 +959,6 @@ class _FunctionGenerator(object):
o = self.opts.copy()
o.update(kwargs)
if len(self.__names) == 1:
- global functions
- if functions is None:
- from sqlalchemy.sql import functions
func = getattr(functions, self.__names[-1].lower(), None)
if func is not None and \
isinstance(func, type) and \
@@ -1205,10 +1204,7 @@ class ClauseElement(Visitable):
dictionary.
"""
- global sql_util
- if sql_util is None:
- from sqlalchemy.sql import util as sql_util
- return sql_util.Annotated(self, values)
+ return sqlutil.Annotated(self, values)
def _deannotate(self):
"""return a copy of this ClauseElement with an empty annotations
@@ -1389,10 +1385,7 @@ class ClauseElement(Visitable):
dialect = self.bind.dialect
bind = self.bind
else:
- global DefaultDialect
- if DefaultDialect is None:
- from sqlalchemy.engine.default import DefaultDialect
- dialect = DefaultDialect()
+ dialect = default.DefaultDialect()
compiler = self._compiler(dialect, bind=bind, **kw)
compiler.compile()
return compiler
@@ -2154,10 +2147,7 @@ class FromClause(Selectable):
"""
- global sql_util
- if sql_util is None:
- from sqlalchemy.sql import util as sql_util
- return sql_util.ClauseAdapter(alias).traverse(self)
+ return sqlutil.ClauseAdapter(alias).traverse(self)
def correspond_on_equivalents(self, column, equivalents):
"""Return corresponding_column for the given column, or if None
@@ -3098,10 +3088,7 @@ class Join(FromClause):
columns = [c for c in self.left.columns] + \
[c for c in self.right.columns]
- global sql_util
- if not sql_util:
- from sqlalchemy.sql import util as sql_util
- self._primary_key.extend(sql_util.reduce_columns(
+ self._primary_key.extend(sqlutil.reduce_columns(
(c for c in columns if c.primary_key), self.onclause))
self._columns.update((col._label, col) for col in columns)
self._foreign_keys.update(itertools.chain(
@@ -3118,14 +3105,11 @@ class Join(FromClause):
return self.left, self.right, self.onclause
def _match_primaries(self, left, right):
- global sql_util
- if not sql_util:
- from sqlalchemy.sql import util as sql_util
if isinstance(left, Join):
left_right = left.right
else:
left_right = None
- return sql_util.join_condition(left, right, a_subset=left_right)
+ return sqlutil.join_condition(left, right, a_subset=left_right)
def select(self, whereclause=None, fold_equivalents=False, **kwargs):
"""Create a :class:`Select` from this :class:`Join`.
@@ -3145,11 +3129,8 @@ class Join(FromClause):
underlying :func:`select()` function.
"""
- global sql_util
- if not sql_util:
- from sqlalchemy.sql import util as sql_util
if fold_equivalents:
- collist = sql_util.folded_equivalents(self)
+ collist = sqlutil.folded_equivalents(self)
else:
collist = [self.left, self.right]
diff --git a/lib/sqlalchemy/test/requires.py b/lib/sqlalchemy/test/requires.py
index 14c548f12..d29b7abc2 100644
--- a/lib/sqlalchemy/test/requires.py
+++ b/lib/sqlalchemy/test/requires.py
@@ -252,6 +252,12 @@ def sane_rowcount(fn):
skip_if(lambda: not testing.db.dialect.supports_sane_rowcount)
)
+def cextensions(fn):
+ return _chain_decorators_on(
+ fn,
+ skip_if(lambda: not _has_cextensions(), "C extensions not installed")
+ )
+
def dbapi_lastrowid(fn):
return _chain_decorators_on(
fn,
@@ -279,7 +285,23 @@ def python2(fn):
"Python version 2.xx is required."
)
)
+
+def python26(fn):
+ return _chain_decorators_on(
+ fn,
+ skip_if(
+ lambda: sys.version_info < (2, 6),
+ "Python version 2.6 or greater is required"
+ )
+ )
+def _has_cextensions():
+ try:
+ from sqlalchemy import cresultproxy, cprocessors
+ return True
+ except ImportError:
+ return False
+
def _has_sqlite():
from sqlalchemy import create_engine
try:
diff --git a/lib/sqlalchemy/test/util.py b/lib/sqlalchemy/test/util.py
index 98667d8c2..e5277f076 100644
--- a/lib/sqlalchemy/test/util.py
+++ b/lib/sqlalchemy/test/util.py
@@ -22,8 +22,6 @@ else:
def lazy_gc():
pass
-
-
def picklers():
picklers = set()
# Py2K
diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py
index ee1fdc67f..9f322d1eb 100644
--- a/lib/sqlalchemy/types.py
+++ b/lib/sqlalchemy/types.py
@@ -34,8 +34,8 @@ from sqlalchemy.sql.visitors import Visitable
from sqlalchemy import util
from sqlalchemy import processors
import collections
+default = util.importlater("sqlalchemy.engine", "default")
-DefaultDialect = None
NoneType = type(None)
if util.jython:
import array
@@ -143,10 +143,7 @@ class AbstractType(Visitable):
mod = ".".join(tokens)
return getattr(__import__(mod).dialects, tokens[-1]).dialect()
else:
- global DefaultDialect
- if DefaultDialect is None:
- from sqlalchemy.engine.default import DefaultDialect
- return DefaultDialect()
+ return default.DefaultDialect()
def __str__(self):
# Py3K
diff --git a/lib/sqlalchemy/util.py b/lib/sqlalchemy/util.py
index 7ac7660a5..c897d30f9 100644
--- a/lib/sqlalchemy/util.py
+++ b/lib/sqlalchemy/util.py
@@ -1563,7 +1563,51 @@ class group_expirable_memoized_property(object):
self.attributes.append(fn.__name__)
return memoized_property(fn)
-
+class importlater(object):
+ """Deferred import object.
+
+ e.g.::
+
+ somesubmod = importlater("mypackage.somemodule", "somesubmod")
+
+ is equivalent to::
+
+ from mypackage.somemodule import somesubmod
+
+ except evaluted upon attribute access to "somesubmod".
+
+ """
+ def __init__(self, path, addtl=None):
+ self._il_path = path
+ self._il_addtl = addtl
+
+ @memoized_property
+ def _il_module(self):
+ m = __import__(self._il_path)
+ for token in self._il_path.split(".")[1:]:
+ m = getattr(m, token)
+ if self._il_addtl:
+ try:
+ return getattr(m, self._il_addtl)
+ except AttributeError:
+ raise AttributeError(
+ "Module %s has no attribute '%s'" %
+ (self._il_path, self._il_addtl)
+ )
+ else:
+ return m
+
+ def __getattr__(self, key):
+ try:
+ attr = getattr(self._il_module, key)
+ except AttributeError:
+ raise AttributeError(
+ "Module %s has no attribute '%s'" %
+ (self._il_path, key)
+ )
+ self.__dict__[key] = attr
+ return attr
+
class WeakIdentityMapping(weakref.WeakKeyDictionary):
"""A WeakKeyDictionary with an object identity index.