Add "is_started" flag to enginefacade

Some module reloading scenarios such as that which occurs
within mod_wsgi mean that an existing module-level enginefacade
is already in the "started" state, however initialization
routines from the calling application may still attempt to
call the ``.configure`` method.  Add a new flag is_started
to both _TransactionContextManager and _TransactionFactory so
that calling code can check for this state ahead of time;
additionally, promote the TypeError raised to a specific subclass
enginefacade.AlreadyStartedError to allow for better optimistic
schemes.

Change-Id: I2f5a9e35c2fae0c28b78beef3dcd2c4794362766
References: I704196711d30c1124e713ac31111a8ea6fa2f1ba
This commit is contained in:
Mike Bayer 2018-10-24 10:11:12 -04:00
parent baa0a045b5
commit 5b36e169ea
3 changed files with 54 additions and 1 deletions

View File

@ -120,6 +120,14 @@ class _Default(object):
hasattr(conf_namespace, key)
class AlreadyStartedError(TypeError):
"""Raises when a factory is being asked to initialize a second time.
Subclasses :class:`.TypeError` for legacy support.
"""
class _TransactionFactory(object):
"""A factory for :class:`._TransactionContext` objects.
@ -314,7 +322,8 @@ class _TransactionFactory(object):
def _configure(self, as_defaults, kw):
if self._started:
raise TypeError("this TransactionFactory is already started")
raise AlreadyStartedError(
"this TransactionFactory is already started")
not_supported = []
for k, v in kw.items():
for dict_ in (
@ -464,6 +473,11 @@ class _TransactionFactory(object):
if self._reader_engine is not self._writer_engine:
self._reader_engine.pool.dispose()
@property
def is_started(self):
"""True if this :class:`._TransactionFactory` is already started."""
return self._started
def _start(self, conf=False, connection=None, slave_connection=None):
with self._start_lock:
# self._started has been checked on the outside
@ -777,6 +791,11 @@ class _TransactionContextManager(object):
"""The :class:`._TransactionFactory` associated with this context."""
return self._root._root_factory
@property
def is_started(self):
"""True if this manager is already started."""
return self._factory.is_started
def configure(self, **kw):
"""Apply configurational options to the factory.

View File

@ -479,6 +479,32 @@ class MockFacadeTest(oslo_test_base.BaseTestCase):
[mock.call.dispose()]
)
def test_started_flag(self):
facade = enginefacade.transaction_context()
self.assertFalse(facade.is_started)
facade.configure(connection=self.engine_uri)
facade.writer.get_engine()
self.assertTrue(facade.is_started)
def test_started_exception(self):
facade = enginefacade.transaction_context()
self.assertFalse(facade.is_started)
facade.configure(connection=self.engine_uri)
facade.writer.get_engine()
exc = self.assertRaises(
enginefacade.AlreadyStartedError,
facade.configure,
connection=self.engine_uri
)
self.assertEqual(
"this TransactionFactory is already started",
exc.args[0]
)
def test_session_reader_decorator(self):
context = oslo_context.RequestContext()

View File

@ -0,0 +1,8 @@
---
features:
- |
Added new ``.is_started`` boolean flag to enginefacade context manager
and factory objects, so that double-configure scenarios can be prevented
by calling code. Additionally, the ``TypeError`` raised when configure
is called after the factory is started is now a specific subclass
``enginefacade.AlreadyStartedError``.