2
0
Fork 0

Allow factory override in make_config()

also add `winsvc` param for `get_config_paths()` to support
RattailFileMonitor service on windows
This commit is contained in:
Lance Edgar 2023-11-21 22:25:45 -06:00
parent ed6a5db452
commit 1431555605
2 changed files with 89 additions and 12 deletions

View file

@ -620,11 +620,12 @@ def generic_default_files(appname):
def get_config_paths( def get_config_paths(
files=None, files=None,
plus_files=None, plus_files=None,
appname='wutta',
env_files_name=None, env_files_name=None,
env_plus_files_name=None, env_plus_files_name=None,
env=None, env=None,
default_files=None, default_files=None,
appname='wutta'): winsvc=None):
""" """
This function determines which files should ultimately be provided This function determines which files should ultimately be provided
to the config constructor. It is normally called by to the config constructor. It is normally called by
@ -696,6 +697,42 @@ def get_config_paths(
files = get_config_paths(default_files=mydefaults) files = get_config_paths(default_files=mydefaults)
:param winsvc: Optional internal name of the Windows service for
which the config object is being made.
This is only needed for true Windows services running via
"Python for Windows Extensions" - which probably only includes
the Rattail File Monitor service.
In this context there is no way to tell the app which config
files to read on startup, so it can only look for "default"
files. But by passing a ``winsvc`` name to this function, it
will first load the default config file, then read a particular
value to determine the "real" config file(s) it should use.
So for example on Windows you might have a config file at
``C:\\ProgramData\\rattail\\rattail.conf`` with contents:
.. code-block:: ini
[rattail.config]
winsvc.RattailFileMonitor = C:\\ProgramData\\rattail\\filemon.conf
And then ``C:\\ProgramData\\rattail\\filemon.conf`` would have
the actual config for the filemon service.
When the service starts it calls::
make_config(winsvc='RattailFileMonitor')
which first reads the ``rattail.conf`` file (since that is the
only sensible default), but then per config it knows to swap
that out for ``filemon.conf`` at startup. This is because it
finds a config value matching the requested service name. The
end result is as if it called this instead::
make_config(files=[r'C:\\ProgramData\\rattail\\filemon.conf'])
:returns: List of file paths. :returns: List of file paths.
""" """
if env is None: if env is None:
@ -748,6 +785,22 @@ def get_config_paths(
# combine all files # combine all files
files.extend(plus_files) files.extend(plus_files)
# when running as a proper windows service, must first read
# "default" file(s) and then consult config to see which file
# should "really" be used. because there isn't a way to specify
# which config file as part of the actual service definition in
# windows, so the service name is used for magic lookup here.
if winsvc:
config = configparser.SafeConfigParser()
config.read(files)
section = f'{appname}.config'
if config.has_section(section):
option = f'winsvc.{winsvc}'
if config.has_option(section, option):
# replace file paths with whatever config value says
files = parse_list(config.get(section, option))
return files return files
@ -759,10 +812,13 @@ def make_config(
env_plus_files_name=None, env_plus_files_name=None,
env=None, env=None,
default_files=None, default_files=None,
winsvc=None,
usedb=None, usedb=None,
preferdb=None, preferdb=None,
factory=None,
extend=True, extend=True,
extension_entry_points=None): extension_entry_points=None,
**kwargs):
""" """
Make a new config object (presumably for global use), initialized Make a new config object (presumably for global use), initialized
per the given parameters and (usually) further modified by all per the given parameters and (usually) further modified by all
@ -771,20 +827,25 @@ def make_config(
This function really does 3 things: This function really does 3 things:
* determine the set of config files to use * determine the set of config files to use
* pass those files to config constructor * pass those files to config factory
* apply extensions to the resulting config object * apply extensions to the resulting config object
Some params are described in :func:`get_config_paths()` since they Some params are described in :func:`get_config_paths()` since they
are passed as-is to that function for the first step. are passed as-is to that function for the first step.
:param appname: The "app name" to use as basis for other things - :param appname: The :term:`app name` to use as basis for other
namely, it affects how config files are located. This name is things - namely, it affects how config files are located. This
also passed to the config constructor at which point it becomes name is also passed to the config factory at which point it
:attr:`wuttjamaican.conf.WuttaConfig.appname`. becomes :attr:`~wuttjamaican.conf.WuttaConfig.appname`.
:param usedb: Passed to the :class:`WuttaConfig` constructor. :param usedb: Passed to the config factory; becomes
:attr:`~wuttjamaican.conf.WuttaConfig.usedb`.
:param preferdb: Passed to the :class:`WuttaConfig` constructor. :param preferdb: Passed to the config factory; becomes
:attr:`~wuttjamaican.conf.WuttaConfig.preferdb`.
:param factory: Optional factory to use when making the object.
Default factory is :class:`WuttaConfig`.
:param extend: Whether to "auto-extend" the config with all :param extend: Whether to "auto-extend" the config with all
registered extensions. registered extensions.
@ -813,11 +874,15 @@ def make_config(
env_files_name=env_files_name, env_files_name=env_files_name,
env_plus_files_name=env_plus_files_name, env_plus_files_name=env_plus_files_name,
env=env, env=env,
default_files=default_files) default_files=default_files,
winsvc=winsvc)
# make config object # make config object
config = WuttaConfig(files, appname=appname, if not factory:
usedb=usedb, preferdb=preferdb) factory = WuttaConfig
config = factory(files, appname=appname,
usedb=usedb, preferdb=preferdb,
**kwargs)
# maybe extend config object # maybe extend config object
if extend: if extend:

View file

@ -417,6 +417,18 @@ class TestGenericDefaultFiles(TestCase):
self.assertEqual(len(files), 0) self.assertEqual(len(files), 0)
class TestGetConfigPaths(FileConfigTestCase):
def test_winsvc(self):
myconf = self.write_file('my.conf', """
[wutta.config]
winsvc.RattailFileMonitor = /path/to/other/file
""")
files = conf.get_config_paths(files=[myconf], winsvc='RattailFileMonitor')
self.assertEqual(files, ['/path/to/other/file'])
class TestMakeConfig(FileConfigTestCase): class TestMakeConfig(FileConfigTestCase):
# nb. we use appname='wuttatest' in this suite to avoid any # nb. we use appname='wuttatest' in this suite to avoid any