Skip to content

Commit

Permalink
Config.launch now supports defining launch_cls entry_points per alias
Browse files Browse the repository at this point in the history
- Enable using a custom entry_point dict not the Site defined one.
  • Loading branch information
MHendricks committed Oct 19, 2023
1 parent 772dafa commit b8a3956
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 17 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ for details on each item.
| Feature | Description | Multiple values |
|---|---|---|
| cli | Used by the hab cli to add extra commands | All unique names are used. |
| launch_cli | 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. | Only the first is used, the rest are discarded. |
| 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) | Only the first is used, the rest are discarded. |

The name of each entry point is used to de-duplicate results from multiple site json files.
This follows the general rule defined in [duplicate definitions](#duplicate-definitions).
Expand Down Expand Up @@ -767,12 +767,19 @@ active hab config, but in this case prepends an additional value on
`ALIASED_GLOBAL_A` will be set to `Global A`. However while you are using the
alias `as_dict`, the variable will be set to `Local A Prepend;Global A`.

Complex Aliases have two keys:
Complex Aliases supports several keys:
1. `cmd` is the command to run. When list or str defined aliases are resolved,
their value is stored under this key.
2. `environment`: A set of env var configuration options. For details on this
format, see [Defining Environments](#defining-environments). This is not
os_specific due to aliases already being defined per-platform.
3. `launch_cls`: If defined this entry_point is used instead of the Site defined
or default class specifically for launching this alias.
See [houdini](tests/distros/houdini19.5/19.5.493/.hab.json) for an example.

Note: Plugins may add support for their own keys.
[Hab-gui](https://github.com/blurstudio/hab-gui#icons-and-labels)
adds icon and label for example.

**Use Case:** You want to add a custom AssetResolver to USD for Maya, Houdini,
and standalone usdview. To get this to work, you need to compile your plugin
Expand Down
26 changes: 18 additions & 8 deletions hab/parsers/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,22 +66,32 @@ def launch(self, alias_name, args=None, blocking=False, cls=None, **kwargs):
Returns:
The created subprocess.Popen instance.
"""
# Construct the command line arguments to execute
if alias_name not in self.aliases:
raise HabError(f'"{alias_name}" is not a valid alias name')
alias = self.aliases[alias_name]

# Get the subprocess.Popen like class to use to launch the alias
if cls is None:
# Respect the site entry point if defined
eps = self.resolver.site.entry_points_for_group("launch_cls")
# Use the entry_point if defined on the alias
alias_cls = alias.get("launch_cls")
if alias_cls:
alias_cls = {"launch_cls": alias_cls}
eps = self.resolver.site.entry_points_for_group(
"launch_cls", entry_points=alias_cls
)
else:
# Otherwise use the global definition from Site
eps = self.resolver.site.entry_points_for_group("launch_cls")

if eps:
cls = eps[0].load()
else:
# Otherwise, default to subprocess.Popen
# Default to subprocess.Popen if not defined elsewhere
from hab.launcher import Launcher

cls = Launcher

# Construct the command line arguments to execute
if alias_name not in self.aliases:
raise HabError(f'"{alias_name}" is not a valid alias name')
alias = self.aliases[alias_name]

try:
cmd = alias["cmd"]
except KeyError:
Expand Down
8 changes: 6 additions & 2 deletions hab/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ 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):
def entry_points_for_group(self, group, default=None, entry_points=None):
"""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 @@ -93,13 +93,17 @@ def entry_points_for_group(self, group, default=None):
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.
"""
# 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.
from importlib_metadata import EntryPoint

ret = []
entry_points = self.get("entry_points", {})
# Use the site defined entry_points if an override dict wasn't provided
if entry_points is None:
entry_points = self.get("entry_points", {})

# Get the entry point definitions, falling back to default if provided
if group in entry_points:
Expand Down
15 changes: 10 additions & 5 deletions tests/distros/houdini19.5/19.5.493/.hab.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,29 @@
],
"windows": [
[
"houdini",
"C:\\Program Files\\Side Effects Software\\Houdini 19.5.493\\bin\\houdini.exe"
"houdini",{
"cmd": "C:\\Program Files\\Side Effects Software\\Houdini 19.5.493\\bin\\houdini.exe",
"launch_cls": {"subprocess": "subprocess:Popen"}
}
],
[
"houdini19.5", {
"cmd": "C:\\Program Files\\Side Effects Software\\Houdini 19.5.493\\bin\\houdini.exe",
"min_verbosity": {"global": 1}
"min_verbosity": {"global": 1},
"launch_cls": {"subprocess": "subprocess:Popen"}
}
],
[
"houdinicore", {
"cmd": "C:\\Program Files\\Side Effects Software\\Houdini 19.5.493\\bin\\houdinicore.exe"
"cmd": "C:\\Program Files\\Side Effects Software\\Houdini 19.5.493\\bin\\houdinicore.exe",
"launch_cls": {"subprocess": "subprocess:Popen"}
}
],
[
"houdinicore19.5", {
"cmd": "C:\\Program Files\\Side Effects Software\\Houdini 19.5.493\\bin\\houdinicore.exe",
"min_verbosity": {"global": 1}
"min_verbosity": {"global": 1},
"launch_cls": {"subprocess": "subprocess:Popen"}
}
],
[
Expand Down
24 changes: 24 additions & 0 deletions tests/test_launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,27 @@ def test_cls_entry_point(config_root):
# Check that specifying cls, overrides site config
proc = cfg.launch("global", blocking=True, cls=Topen)
assert type(proc) is Topen


def test_alias_entry_point(config_root):
"""Check that if an entry point is defined on a complex alias, it is used."""
site = Site(
[config_root / "site/site_entry_point_a.json", config_root / "site_main.json"]
)

resolver = Resolver(site=site)
cfg = resolver.resolve("app/aliased/mod")

# NOTE: We need to compare the name of the classes because they are separate
# imports that don't compare equal using `is`.

# Check that entry_point site config is respected
proc = cfg.launch("global", blocking=True)
assert type(proc).__name__ == "Popen"

# Check that if the complex alias specifies launch_cls, it is used instead
# of the site defined or default class.
alias = cfg.frozen_data["aliases"][utils.Platform.name()]["global"]
alias["launch_cls"] = {"subprocess": "tests.test_launch:Topen"}
proc = cfg.launch("global", blocking=True)
assert type(proc).__name__ == "Topen"

0 comments on commit b8a3956

Please sign in to comment.