From a25712ef54fdfe21fd0e2d4f73f4fa8c0ab93e6a Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 4 Jul 2024 07:12:22 -0500 Subject: [PATCH] fix: let config class specify default app handler, engine maker this avoids the need for a config subclass to use `setdefault()` hacks to specify default app handler for instance, since that approach must compete with config extensions who also may wish to do that. similar concept for the engine maker; notably the rattail project needs to override this function somewhat and we need a way to allow for that without (re-)introducing the app handler here. --- src/wuttjamaican/conf.py | 32 +++++++++++++++++++++++++++++++- src/wuttjamaican/db/conf.py | 6 ++++-- tests/test_conf.py | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/wuttjamaican/conf.py b/src/wuttjamaican/conf.py index b7dfe8f..9013d57 100644 --- a/src/wuttjamaican/conf.py +++ b/src/wuttjamaican/conf.py @@ -126,6 +126,23 @@ class WuttaConfig: it's useful, but in practice you should not update it directly; instead use :meth:`setdefault()`. + .. attribute:: default_app_handler_spec + + Spec string for the default app handler, if config does not + specify to use another. + + The true default for this is ``'wuttjamaican.app:AppHandler'`` + (aka. :class:`~wuttjamaican.app.AppHandler`). + + .. attribute:: default_engine_maker_spec + + Spec string for the default engine maker function, if config + does not specify to use another. + + The true default for this is + ``'wuttjamaican.db.conf:make_engine_from_config'`` (aka. + :func:`~wuttjamaican.db.conf.make_engine_from_config()`). + .. attribute:: files_read List of all INI config files which were read on app startup. @@ -165,6 +182,8 @@ class WuttaConfig: See also :ref:`where-config-settings-come-from`. """ + default_app_handler_spec = 'wuttjamaican.app:AppHandler' + default_engine_maker_spec = 'wuttjamaican.db.conf:make_engine_from_config' def __init__( self, @@ -572,11 +591,22 @@ class WuttaConfig: """ if not hasattr(self, '_app'): spec = self.get(f'{self.appname}.app.handler', usedb=False, - default='wuttjamaican.app:AppHandler') + default=self.default_app_handler_spec) factory = load_object(spec) self._app = factory(self) return self._app + def get_engine_maker(self): + """ + Returns a callable to be used for constructing SQLAlchemy + engines fromc config. + + Which callable is used depends on + :attr:`default_engine_maker_spec` but by default will be + :func:`wuttjamaican.db.conf.make_engine_from_config()`. + """ + return load_object(self.default_engine_maker_spec) + class WuttaConfigExtension: """ diff --git a/src/wuttjamaican/db/conf.py b/src/wuttjamaican/db/conf.py index 67573ad..769df3e 100644 --- a/src/wuttjamaican/db/conf.py +++ b/src/wuttjamaican/db/conf.py @@ -68,16 +68,18 @@ def get_engines(config, prefix): else: keys = ['default'] + make_engine = config.get_engine_maker() + engines = OrderedDict() cfg = config.get_dict(prefix) for key in keys: key = key.strip() try: - engines[key] = make_engine_from_config(cfg, prefix=f'{key}.') + engines[key] = make_engine(cfg, prefix=f'{key}.') except KeyError: if key == 'default': try: - engines[key] = make_engine_from_config(cfg, prefix='sqlalchemy.') + engines[key] = make_engine(cfg, prefix='sqlalchemy.') except KeyError: pass return engines diff --git a/tests/test_conf.py b/tests/test_conf.py index 4127fe8..5de11b2 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -10,6 +10,7 @@ import sqlalchemy as sa from wuttjamaican import conf from wuttjamaican.exc import ConfigurationError from wuttjamaican.db import Session +from wuttjamaican.db.conf import make_engine_from_config from wuttjamaican.app import AppHandler from wuttjamaican.testing import FileConfigTestCase @@ -386,12 +387,43 @@ configure_logging = true self.assertEqual(config.get_list('foo.bar'), ['hello', 'world']) def test_get_app(self): + + # default handler config = conf.WuttaConfig() + self.assertEqual(config.default_app_handler_spec, 'wuttjamaican.app:AppHandler') app = config.get_app() - # make sure we get the true default handler class self.assertIsInstance(app, AppHandler) + # nb. make extra sure we didn't get a subclass self.assertIs(type(app), AppHandler) + # custom default handler + config = conf.WuttaConfig() + config.default_app_handler_spec = 'tests.test_conf:CustomAppHandler' + app = config.get_app() + self.assertIsInstance(app, CustomAppHandler) + + def test_get_engine_maker(self): + + # default func + config = conf.WuttaConfig() + self.assertEqual(config.default_engine_maker_spec, 'wuttjamaican.db.conf:make_engine_from_config') + make_engine = config.get_engine_maker() + self.assertIs(make_engine, make_engine_from_config) + + # custom default func + config = conf.WuttaConfig() + config.default_engine_maker_spec = 'tests.test_conf:custom_make_engine_from_config' + make_engine = config.get_engine_maker() + self.assertIs(make_engine, custom_make_engine_from_config) + + +class CustomAppHandler(AppHandler): + pass + + +def custom_make_engine_from_config(*args, **kwargs): + pass + class TestWuttaConfigExtension(TestCase):