From: Mike Bayer Date: Wed, 21 Sep 2011 20:56:14 +0000 (-0400) Subject: - Adjusted the "importlater" mechanism, which is X-Git-Tag: rel_0_7_3~41 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2aa80d40d2c9a00bb87a145bba1f01c327b6000b;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Adjusted the "importlater" mechanism, which is used internally to resolve import cycles, such that the usage of __import__ is completed when the import of sqlalchemy or sqlalchemy.orm is done, thereby avoiding any usage of __import__ after the application starts new threads, fixes [ticket:2279]. Also in 0.6.9. --- diff --git a/CHANGES b/CHANGES index 4d16c708dd..5b9ed4c96b 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,15 @@ CHANGES ======= 0.7.3 ===== +- general + - Adjusted the "importlater" mechanism, which is + used internally to resolve import cycles, + such that the usage of __import__ is completed + when the import of sqlalchemy or sqlalchemy.orm + is done, thereby avoiding any usage of __import__ + after the application starts new threads, + fixes [ticket:2279]. Also in 0.6.9. + - orm - Added after_soft_rollback() Session event. This event fires unconditionally whenever rollback() diff --git a/lib/sqlalchemy/__init__.py b/lib/sqlalchemy/__init__.py index c15cc6b693..0b9b7a00e9 100644 --- a/lib/sqlalchemy/__init__.py +++ b/lib/sqlalchemy/__init__.py @@ -120,3 +120,6 @@ __all__ = sorted(name for name, obj in locals().items() __version__ = '0.7.3' del inspect, sys + +from sqlalchemy import util as _sa_util +_sa_util.importlater.resolve_all() \ No newline at end of file diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py index f6b02eaa83..b7f7884f24 100644 --- a/lib/sqlalchemy/orm/__init__.py +++ b/lib/sqlalchemy/orm/__init__.py @@ -1458,3 +1458,6 @@ def undefer_group(name): """ return strategies.UndeferGroupOption(name) + +from sqlalchemy import util as _sa_util +_sa_util.importlater.resolve_all() \ No newline at end of file diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py index 893d6ea2a7..334d6d3786 100644 --- a/lib/sqlalchemy/orm/interfaces.py +++ b/lib/sqlalchemy/orm/interfaces.py @@ -21,7 +21,7 @@ from itertools import chain from sqlalchemy import exc as sa_exc from sqlalchemy import util from sqlalchemy.sql import operators -deque = util.importlater('collections').deque +deque = __import__('collections').deque mapperutil = util.importlater('sqlalchemy.orm', 'util') diff --git a/lib/sqlalchemy/sql/__init__.py b/lib/sqlalchemy/sql/__init__.py index c591e6802e..1b82847814 100644 --- a/lib/sqlalchemy/sql/__init__.py +++ b/lib/sqlalchemy/sql/__init__.py @@ -64,3 +64,4 @@ from sqlalchemy.sql.visitors import ClauseVisitor __tmp = locals().keys() __all__ = sorted([i for i in __tmp if not i.startswith('__')]) + diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py index 4dd9a52706..cf8b2acac8 100644 --- a/lib/sqlalchemy/util/langhelpers.py +++ b/lib/sqlalchemy/util/langhelpers.py @@ -527,37 +527,67 @@ class importlater(object): from mypackage.somemodule import somesubmod except evaluted upon attribute access to "somesubmod". + + importlater() currently requires that resolve_all() be + called, typically at the bottom of a package's __init__.py. + This is so that __import__ still called only at + module import time, and not potentially within + a non-main thread later on. """ + + _unresolved = set() + def __init__(self, path, addtl=None): self._il_path = path self._il_addtl = addtl + importlater._unresolved.add(self) + + @classmethod + def resolve_all(cls): + for m in list(importlater._unresolved): + m._resolve() + + @property + def _full_path(self): + if self._il_addtl: + return self._il_path + "." + self._il_addtl + else: + return self._il_path @memoized_property def module(self): + if self in importlater._unresolved: + raise ImportError( + "importlater.resolve_all() hasn't been called") + + m = self._initial_import if self._il_addtl: - m = __import__(self._il_path, globals(), locals(), - [self._il_addtl]) - try: - return getattr(m, self._il_addtl) - except AttributeError: - raise ImportError( - "Module %s has no attribute '%s'" % - (self._il_path, self._il_addtl) - ) + m = getattr(m, self._il_addtl) else: - m = __import__(self._il_path) for token in self._il_path.split(".")[1:]: m = getattr(m, token) - return m + return m + + def _resolve(self): + importlater._unresolved.discard(self) + if self._il_addtl: + self._initial_import = __import__( + self._il_path, globals(), locals(), + [self._il_addtl]) + else: + self._initial_import = __import__(self._il_path) def __getattr__(self, key): + if key == 'module': + raise ImportError("Could not resolve module %s" + % self._full_path) try: attr = getattr(self.module, key) except AttributeError: raise AttributeError( "Module %s has no attribute '%s'" % - (self._il_path, key) + (self._full_path, key) ) self.__dict__[key] = attr return attr