diff --git a/README.md b/README.md index cb5c153..87a8cd9 100644 --- a/README.md +++ b/README.md @@ -438,6 +438,8 @@ for details on each item. | [Group][tt-group] | Description | [\*\*kwargs][tt-kwargs] | [Return][tt-return] | [Multiple][tt-multi] | |---|---|---|---|---| | hab.cli | Used by the hab cli to add extra commands. This is expected to be a `click.command` or `click.group` decorated function. | | | [All][tt-multi-all] | +| hab.cfg.reduce.env | Used to make any modifications to a config after the global env is resolved but before aliases are resolved. | `cfg` | | [All][tt-multi-all] | +| hab.cfg.reduce.finalize | Used to make any modifications to a config after aliases are resolved and just before the the config finishes reducing. | `cfg` | | [All][tt-multi-all] | | hab.launch_cls | Used as the default `cls` by `hab.parsers.Config.launch()` to launch aliases from inside of python. This should be a subclass of subprocess.Popen. A [complex alias](#complex-aliases) may override this per alias. Defaults to [`hab.launcher.Launcher`](hab/launcher.py). [Example](tests/site/site_entry_point_a.json) | | | [First][tt-multi-first] | | hab.uri.validate | Used to validate and modify a URI. If the URI is invalid, this should raise an exception. If the URI should be modified, then return the modified URI as a string. | `resolver`, `uri` | Updated URI as string or None. | [All][tt-multi-all] | diff --git a/hab/parsers/flat_config.py b/hab/parsers/flat_config.py index 0d10a81..c3e8ba0 100644 --- a/hab/parsers/flat_config.py +++ b/hab/parsers/flat_config.py @@ -35,6 +35,9 @@ def _finalize_values(self): # This call ensures that `self.frozen_data["environment"]` is populated. self.environment + # Run any configured entry_points before aliases are calculated + self.resolver.site.run_entry_points_for_group("hab.cfg.reduce.env", cfg=self) + # Process version aliases, merging global env vars. platform_aliases = {} self.frozen_data["aliases"] = platform_aliases @@ -44,6 +47,11 @@ def _finalize_values(self): ): platform_aliases.setdefault(platform, {})[alias] = data + # Run any configured entry_points before finishing + self.resolver.site.run_entry_points_for_group( + "hab.cfg.reduce.finalize", cfg=self + ) + def _process_version(self, version, existing=None): """Generator that yields each finalized alias definition dictionary to be stored in the frozen_data for aliases. diff --git a/hab/site.py b/hab/site.py index eb6becd..8abff28 100644 --- a/hab/site.py +++ b/hab/site.py @@ -1,3 +1,4 @@ +import logging import os from collections import UserDict from pathlib import Path, PurePosixPath, PureWindowsPath @@ -5,6 +6,8 @@ from . import utils from .merge_dict import MergeDict +logger = logging.getLogger(__name__) + class Site(UserDict): """Provides site configuration to hab. @@ -184,6 +187,28 @@ def platform_path_map(self, path, platform=None): return str(path) + def run_entry_points_for_group( + self, group, default=None, entry_points=None, **kwargs + ): + """Iterates over `entry_points_for_group` calling the resolved object. + + Args: + group (str): The name of the group of entry_points to process. + default (dict, optional): If the entry_point is not defined, return + the entry points defined by this dictionary. This is the contents + of the entry_points group, not the entire entry_points dict. For + example: `{"gui": "hab_gui.cli:gui"}` + entry_points (dict, optional): Use this dictionary instead of the one + defined on this Site object. + **kwargs: Any other kwargs are passed to the loaded entry_point record. + """ + for ep in self.entry_points_for_group( + group, default=default, entry_points=entry_points + ): + logger.debug(f"Running {group} entry_point: {ep}") + func = ep.load() + func(**kwargs) + def standardize_platform_path_maps(self): """Ensure the mappings defined in platform_path_maps are converted to the correct PurePath classes.""" diff --git a/tests/hab_test_entry_points.py b/tests/hab_test_entry_points.py index c10374d..8582d3f 100644 --- a/tests/hab_test_entry_points.py +++ b/tests/hab_test_entry_points.py @@ -9,6 +9,22 @@ def gui_alt(): raise NotImplementedError("hab_test_entry_points.gui_alt called successfully") +def cfg_reduce_env(cfg): + """Used to test that an entry point is called by raising an exception when + called. See `tests/site/eps/README.md` for details.""" + raise NotImplementedError( + "hab_test_entry_points.cfg_reduce_env called successfully" + ) + + +def cfg_reduce_finalize(cfg): + """Used to test that an entry point is called by raising an exception when + called. See `tests/site/eps/README.md` for details.""" + raise NotImplementedError( + "hab_test_entry_points.cfg_reduce_finalize called successfully" + ) + + def uri_validate_error(resolver, uri): """Used to test that an entry point is called by raising an exception when called. See `tests/site/eps/README.md` for details.""" diff --git a/tests/site/eps/cfg_reduce_env.json b/tests/site/eps/cfg_reduce_env.json new file mode 100644 index 0000000..7880645 --- /dev/null +++ b/tests/site/eps/cfg_reduce_env.json @@ -0,0 +1,9 @@ +{ + "append": { + "entry_points": { + "hab.cfg.reduce.env": { + "a": "hab_test_entry_points:cfg_reduce_env" + } + } + } +} diff --git a/tests/site/eps/cfg_reduce_finalize.json b/tests/site/eps/cfg_reduce_finalize.json new file mode 100644 index 0000000..329c208 --- /dev/null +++ b/tests/site/eps/cfg_reduce_finalize.json @@ -0,0 +1,9 @@ +{ + "append": { + "entry_points": { + "hab.cfg.reduce.finalize": { + "a": "hab_test_entry_points:cfg_reduce_finalize" + } + } + } +} diff --git a/tests/test_site.py b/tests/test_site.py index c8d8c70..acf117c 100644 --- a/tests/test_site.py +++ b/tests/test_site.py @@ -522,6 +522,14 @@ def test_site_cli(self, config_root, site_files, import_name, fname): @pytest.mark.parametrize( "site_file,except_match", ( + ( + "cfg_reduce_env.json", + "hab_test_entry_points.cfg_reduce_env called successfully", + ), + ( + "cfg_reduce_finalize.json", + "hab_test_entry_points.cfg_reduce_finalize called successfully", + ), ( "cfg_uri_validate.json", "hab_test_entry_points.uri_validate_error called successfully",