Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for disabling an existing entry_point by setting it to null #91

Merged
merged 1 commit into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,53 @@ This follows the general rule defined in [duplicate definitions](#duplicate-defi
Entry_point names should start with `hab.` and use `.` between each following word
following the group specification on https://packaging.python.org/en/latest/specifications/entry-points/#data-model.

##### Overriding Entry Points

When using multiple site config files you may end up needing to disable a entry point
in specific conditions. For example you want to enable
[hab-gui's cli integration](https://github.com/blurstudio/hab-gui) by default,
but need to disable it for linux farm nodes that have no gui enabled. If you set
a entry point's value to `null` hab will ignore it and not attempt to load it.

For example if you have your `HAB_PATH` set to `c:\hab\host.json;\\server\share\studio.json`.
- The `host.json` site file is stored on each workstation and adds distros that
are not able to be run from over the network.
- The `studio.json` site file that is shared on the network for ease of deployment.
It is used by all hab users on all platforms to define most of the studio defaults
including adding the hab-gui cli as well as the URI configs.

```json5
// studio.json
{
"append": {
"entry_points": {
"cli": {
"gui": "hab_gui.cli:gui"
}
}
}
}
```

For workstations that don't support a gui you modify the workstations `host.json`
site file, adding a override of the value from `"hab_gui.cli:gui"` to `null`.
```json5
// host.json
{
"append": {
"entry_points": {
"hab.cli": {
"gui": null
}
}
}
}
```
Alternatively, you could create a second host site file named `c:\hab\host_no_gui.json`
put the gui disabling config in that file and on the host's you want to disable
the gui prepend to `HAB_PATHS=c:\hab\host_no_gui.json;c:\hab\host.json;\\server\share\studio.json`.


### Python version

Hab uses shell script files instead of an entry_point executable. This allows it
Expand Down
10 changes: 9 additions & 1 deletion hab/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ def dump(self, verbosity=0, color=None):
ret = "\n".join(ret)
return utils.dump_title("Dump of Site", f"{site_ret}\n{ret}", color=color)

def entry_points_for_group(self, group, default=None, entry_points=None):
def entry_points_for_group(
self, group, default=None, entry_points=None, omit_none=True
):
"""Returns a list of importlib_metadata.EntryPoint objects enabled by
this site config. To import and resolve the defined object call `ep.load()`.

Expand All @@ -98,6 +100,9 @@ def entry_points_for_group(self, group, default=None, entry_points=None):
example: `{"gui": "hab_gui.cli:gui"}`
entry_points (dict, optional): Use this dictionary instead of the one
defined on this Site object.
omit_none (bool, optional): If an entry_point's value is set to null/None
then don't include an EntryPoint object for it in the return. This
allows a second site file to disable a entry_point already set.
"""
# Delay this import to when required. It's faster than pkg_resources but
# no need to pay the import price for it if you are not using it.
Expand All @@ -119,6 +124,9 @@ def entry_points_for_group(self, group, default=None, entry_points=None):
# using it's `entry_points` function. We want the current site configuration
# to define the entry points being loaded not the installed pip packages.
for name, value in ep_defs.items():
if omit_none and value is None:
continue

ep = EntryPoint(name=name, group=group, value=value)
ret.append(ep)
return ret
Expand Down
9 changes: 9 additions & 0 deletions tests/site/site_entry_point_c.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"append": {
"entry_points": {
"hab.cli": {
"test-gui": null
}
}
}
}
46 changes: 41 additions & 5 deletions tests/test_site.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,32 +482,68 @@ def test_default(self, config_root):
assert ep.value == "case:func"

@pytest.mark.parametrize(
"site_files,import_name,fname",
# Note: The default for omit_none is True
"site_files,import_name,fname,omit_none",
(
(["site/site_entry_point_a.json"], "hab_test_entry_points", "gui"),
(["site/site_entry_point_a.json"], "hab_test_entry_points", "gui", True),
(
["site/site_entry_point_b.json", "site/site_entry_point_a.json"],
"hab_test_entry_points",
"gui_alt",
True,
),
(
["site/site_entry_point_a.json", "site/site_entry_point_b.json"],
"hab_test_entry_points",
"gui",
True,
),
# Tests handling an entry_point value of None
(
# None value is ignored due to order
["site/site_entry_point_a.json", "site/site_entry_point_c.json"],
"hab_test_entry_points",
"gui",
True,
),
(
# None value is used, but ignored due to omit_none setting
["site/site_entry_point_c.json", "site/site_entry_point_a.json"],
None,
None,
True,
),
(
# None value is used, but still returned due to omit_none setting
["site/site_entry_point_c.json", "site/site_entry_point_a.json"],
None,
None,
False,
),
),
)
def test_site_cli(self, config_root, site_files, import_name, fname):
def test_site_cli(self, config_root, site_files, import_name, fname, omit_none):
"""Test a site defining an entry point for `hab.cli`, possibly multiple times."""
site = Site([config_root / f for f in site_files])
entry_points = site.entry_points_for_group("hab.cli")
entry_points = site.entry_points_for_group("hab.cli", omit_none=omit_none)

if import_name is None and omit_none is True:
assert len(entry_points) == 0
# Nothing else to test if the value is null
return

assert len(entry_points) == 1

# Test that the `test-gui` `hab.cli` entry point is handled correctly
ep = entry_points[0]
assert ep.name == "test-gui"
assert ep.group == "hab.cli"
assert ep.value == f"{import_name}:{fname}"
if omit_none is False:
assert ep.value is None
# Noting else to test, we can't load a value of None.
return
else:
assert ep.value == f"{import_name}:{fname}"

# Load the module's function
funct = ep.load()
Expand Down