diff options
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r-- | lib/sqlalchemy/cextension/resultproxy.c | 8 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/sqlite/base.py | 16 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/base.py | 8 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/attributes.py | 7 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/mapper.py | 35 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/properties.py | 3 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/session.py | 5 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/strategies.py | 8 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/unitofwork.py | 10 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/util.py | 33 | ||||
-rw-r--r-- | lib/sqlalchemy/schema.py | 19 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/compiler.py | 7 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/expression.py | 41 | ||||
-rw-r--r-- | lib/sqlalchemy/test/requires.py | 22 | ||||
-rw-r--r-- | lib/sqlalchemy/test/util.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/types.py | 7 | ||||
-rw-r--r-- | lib/sqlalchemy/util.py | 46 |
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. |