Add support for menuinst.toml#477
Conversation
| # Pass path so install/remove records the actual filename in menuinst.toml | ||
| if remove: | ||
| _api_remove(metadata, target_prefix=prefix, **kwargs) | ||
| _api_remove(json_path, target_prefix=prefix, **kwargs) |
There was a problem hiding this comment.
This change was done to properly register the file name the shortcuts originate from in the toml file. There are other ways to achieve the same behavior but this approach required the least intrusive code changes.
marcoesters
left a comment
There was a problem hiding this comment.
This is nice - I am a little worried about the special-casing of DISTRIBUTION_NAME and made an alternative suggestion. I haven't thought through all the ramifications of that suggestion yet though.
| from ..api import _install_adapter, write_menuinst_toml | ||
| from ..utils import read_menuinst_toml |
There was a problem hiding this comment.
I think I understand why you're doing this ("public" API vs "private" utility function), but it looks awkward to have the read and write functions in different modules. I'm not sure if we should add the read function to the API just because of that though.
There was a problem hiding this comment.
Yeah the intent was that the utils is read-only but the other ones actually cause changes. I originally had them all n api.py but decided last minute to move it. I'm fine with either. Proposals:
- Move both to utils.py (keep read/write together)
- Move both to api.py (where mutations happen)
- Keep as-is and document the rationale
There was a problem hiding this comment.
I don't have too strong of an opinion either way, but 2. for the "main" TOML functions (top-level read and write) sounds the most sensible to me.
| # The env var allows installers to set distribution_name dynamically at | ||
| # install time. We must persist it here because the env var is transient | ||
| # and may not be set when packages with shortcuts are installed later. | ||
| if env_name := os.environ.get("MENUINST_DISTRIBUTION_NAME"): |
There was a problem hiding this comment.
Let's call it distribution_name to be consistent.
I'm wondering though if it makes sense to record all MENUINST_* environment variables here.
I'm just generally wondering how we would distinguish between those "global" parameters that should persist vs. parameters that should be recalculated during install-time. Maybe we need a separate command for that? menuinst --set-global-placeholder or something similar?
There was a problem hiding this comment.
I think the generalization is interesting but comes with additional complexity, which I would propose we postpone until we have concrete use cases. Refactoring this wouldn't be difficult but I'm afraid we run into fixing something where we don't have any real use case (if that makes sense).
Also renamed the variable: d5324fb
There was a problem hiding this comment.
I was thinking about #334 in particular. Or the environment placeholder in the Anaconda Prompt menus or the package version placeholders for spyder. Right now, this needs to be implemented with a sed-like workaround for Windows.
Setting variables like this via menuinst and then using placeholder syntax would be really convenient and remove all special casing. Installers would then just call _conda.exe menuinst --set-(global|local)-placeholder CUSTOM_PLACEHOLDER=something (or similar).
| "BASE_PREFIX": str(self.base_prefix), | ||
| "DISTRIBUTION_NAME": self.base_prefix.name, | ||
| "DISTRIBUTION_NAME": self._get_distribution_name(), | ||
| "BASE_PYTHON": str(self.base_prefix / "bin" / "python"), |
There was a problem hiding this comment.
Since we already have MENUINST_BASE_PREFIX and the like, I wonder if we can generalize overwriting those placeholders instead of special-casing DISTRIBUTION_NAME (even though BASE_PREFIX and PREFIX are special cases).
There was a problem hiding this comment.
Yeah my answer to this is similar to above. I think the idea is good but something I think we can do better once we have concrete use cases. I would also argue that MENUINST_BASE_PREFIX and MENUINST_PREFIX are runtime path overrides and are slightly different from DISTRIBUTION_NAME in this scenario.
| # Write distribution_name only to base prefix, and only if not already set | ||
| if prefix.samefile(base_prefix) and distribution_name: | ||
| data.setdefault("distribution_name", distribution_name) |
There was a problem hiding this comment.
I think this illustrates the brittleness of the special casing a little bit. If we come up with other variables that must only be written into the base prefix, we have to keep making sure that they are in the correct place. Using the menuinst --set-global-placeholder would make make this more stable.
There was a problem hiding this comment.
Yeah I think that's definitely a valid concern. I think in its current state with only one variable, it's doable, but if we add more I think definitely we can iterate on the design to something more robust. I've been trying to keep it simple and avoid over-engineering.
| import tomli as tomllib | ||
|
|
||
| logger = getLogger(__name__) | ||
| MENUINST_TOML_SCHEMA_VERSION = 1 |
There was a problem hiding this comment.
I suspect we will need to be more fine-grained than that?
There was a problem hiding this comment.
You mean something like 1.0.0? Absolutely, I just made it simple (1) right now to illustrate the purpose.
There was a problem hiding this comment.
I would use SchemaVer like we do for the menuinst schema: https://docs.snowplow.io/docs/api-reference/iglu/common-architecture/schemaver/
| except Exception as e: | ||
| logger.warning("Failed to read menuinst.toml from %s: %s", toml_path, e) | ||
| return {} |
There was a problem hiding this comment.
I think we should actually error out here. The only way I can reasonably imagine that the file is invalid is if someone manipulated the file manually. In that case, returning {} may result in unexpected behavior.
| TODO: Use the recorded paths as the source of truth for shortcut removal, | ||
| instead of recomputing paths from menu JSON metadata. This would handle | ||
| cases where shortcuts were moved or the menu JSON changed. |
There was a problem hiding this comment.
I may be missing some of the complexity here, but I think this is well in scope of this PR. All the pieces are already there.
There was a problem hiding this comment.
I added this TODO to help the reviewers and keep it simple. Merging this wouldn't break any existing behavior. I'm happy to do it in this PR but perhaps even opening a separate PR targeting this branch would be more feasible, let me know what you prefer.
There was a problem hiding this comment.
A separate PR targeting this branch would work for me. As of this PR, all we do with those recorded paths is remove them from the record on uninstallation. So, the records currently don't find much use.
| def test_cli_env_var_persistence( | ||
| tmp_path: Path, monkeypatch: pytest.MonkeyPatch, env_var_set: bool, expect_toml: bool | ||
| ) -> None: | ||
| """Test that MENUINST_DISTRIBUTION_NAME env var is persisted to menuinst.toml.""" |
There was a problem hiding this comment.
| """Test that MENUINST_DISTRIBUTION_NAME env var is persisted to menuinst.toml.""" | |
| """Test that MENUINST_DISTRIBUTION_NAME env var persists in menuinst.toml.""" |
|
@marcoesters for the second round of review, see my responses below (trying to avoid spamming with email notifications):
|
Summary
Adds the possibility to override
DISTRIBUTION_NAMEand improves tracking of installed shortcuts.Changes
MENUINST_DISTRIBUTION_NAMEenv var support with resolution order:MENUINST_DISTRIBUTION_NAMEenv var (highest priority)$BASE_PREFIX/Menu/menuinst.tomlvaluebase_prefix.name(fallback)menuinst.tomlfor persistent storage ofdistribution_nameand shortcut trackingread_menuinst_toml()inutils.pyandwrite_menuinst_toml()inapi.pyrecord_shortcuts()/remove_shortcut_records()called frominstall()/remove()tomli(py<3.11) andtomli-wdependenciesPlanned changes once on main
MENUINST_DISTRIBUTION_NAME=%APPNAME%inrun_installation.batfor MSI installersinstall()/remove()with real menu JSON to verify end-to-end TOML recordingremove()to use the recorded shortcut paths frommenuinst.tomlwhenever possible instead of recomputing from menu JSON metadataChecklist - did you ...
newsdirectory (using the template) for the next release's release notes?