diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2018-05-27 13:45:20 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2018-05-28 09:28:12 -0400 |
commit | 2ac7cad7179ff594d8bb3df9856c891bf4e51097 (patch) | |
tree | 91b60206e44e64748b971d339fd620a217415f4c /lib/sqlalchemy/ext/automap.py | |
parent | c7ae04d1c5c4aa6c6099584ae386d6ab9ef7b290 (diff) | |
download | sqlalchemy-2ac7cad7179ff594d8bb3df9856c891bf4e51097.tar.gz |
Mutex on _CONFIGURE_MUTEX in automap.prepare()
Fixed a race condition which could occur if automap
:meth:`.AutomapBase.prepare` were used within a multi-threaded context
against other threads which may call :func:`.configure_mappers` as a
result of use of other mappers. The unfinished mapping work of automap
is particularly sensitive to being pulled in by a
:func:`.configure_mappers` step leading to errors.
Change-Id: I6d36df6639bf5cb8f137187dff68f386f5e84f88
Fixes: #4266
Diffstat (limited to 'lib/sqlalchemy/ext/automap.py')
-rw-r--r-- | lib/sqlalchemy/ext/automap.py | 90 |
1 files changed, 48 insertions, 42 deletions
diff --git a/lib/sqlalchemy/ext/automap.py b/lib/sqlalchemy/ext/automap.py index 30f48fcea..cafb3d61c 100644 --- a/lib/sqlalchemy/ext/automap.py +++ b/lib/sqlalchemy/ext/automap.py @@ -518,6 +518,7 @@ from .declarative.base import _DeferredMapperConfig from ..sql import and_ from ..schema import ForeignKeyConstraint from ..orm import relationship, backref, interfaces +from ..orm.mapper import _CONFIGURE_MUTEX from .. import util @@ -754,49 +755,54 @@ class AutomapBase(object): autoload_replace=False ) - table_to_map_config = dict( - (m.local_table, m) - for m in _DeferredMapperConfig. - classes_for_base(cls, sort=False) - ) - - many_to_many = [] + _CONFIGURE_MUTEX.acquire() + try: + table_to_map_config = dict( + (m.local_table, m) + for m in _DeferredMapperConfig. + classes_for_base(cls, sort=False) + ) - for table in cls.metadata.tables.values(): - lcl_m2m, rem_m2m, m2m_const = _is_many_to_many(cls, table) - if lcl_m2m is not None: - many_to_many.append((lcl_m2m, rem_m2m, m2m_const, table)) - elif not table.primary_key: - continue - elif table not in table_to_map_config: - mapped_cls = type( - classname_for_table(cls, table.name, table), - (cls, ), - {"__table__": table} - ) - map_config = _DeferredMapperConfig.config_for_cls(mapped_cls) - cls.classes[map_config.cls.__name__] = mapped_cls - table_to_map_config[table] = map_config - - for map_config in table_to_map_config.values(): - _relationships_for_fks(cls, - map_config, - table_to_map_config, - collection_class, - name_for_scalar_relationship, - name_for_collection_relationship, - generate_relationship) - - for lcl_m2m, rem_m2m, m2m_const, table in many_to_many: - _m2m_relationship(cls, lcl_m2m, rem_m2m, m2m_const, table, - table_to_map_config, - collection_class, - name_for_scalar_relationship, - name_for_collection_relationship, - generate_relationship) - - for map_config in _DeferredMapperConfig.classes_for_base(cls): - map_config.map() + many_to_many = [] + + for table in cls.metadata.tables.values(): + lcl_m2m, rem_m2m, m2m_const = _is_many_to_many(cls, table) + if lcl_m2m is not None: + many_to_many.append((lcl_m2m, rem_m2m, m2m_const, table)) + elif not table.primary_key: + continue + elif table not in table_to_map_config: + mapped_cls = type( + classname_for_table(cls, table.name, table), + (cls, ), + {"__table__": table} + ) + map_config = _DeferredMapperConfig.config_for_cls( + mapped_cls) + cls.classes[map_config.cls.__name__] = mapped_cls + table_to_map_config[table] = map_config + + for map_config in table_to_map_config.values(): + _relationships_for_fks(cls, + map_config, + table_to_map_config, + collection_class, + name_for_scalar_relationship, + name_for_collection_relationship, + generate_relationship) + + for lcl_m2m, rem_m2m, m2m_const, table in many_to_many: + _m2m_relationship(cls, lcl_m2m, rem_m2m, m2m_const, table, + table_to_map_config, + collection_class, + name_for_scalar_relationship, + name_for_collection_relationship, + generate_relationship) + + for map_config in _DeferredMapperConfig.classes_for_base(cls): + map_config.map() + finally: + _CONFIGURE_MUTEX.release() _sa_decl_prepare = True """Indicate that the mapping of classes should be deferred. |