Do not treat a named environment as the base environment - #538
Draft
hmaarrfk wants to merge 3 commits into
Draft
Conversation
menuinst decides whether the target prefix is the base environment by
comparing it against `base_prefix`, which follows the running interpreter
via `DEFAULT_BASE_PREFIX`. A conda installed *into* an environment reports
that environment as its own base:
$ <root>/envs/myenv/bin/conda info --base
<root>/envs/myenv
so anything driving menuinst from such an environment passes it as both the
target prefix and the base prefix. `{{ ENV_NAME }}` then renders as `base`,
and a `name` given as a base/non-base object resolves to its
`target_environment_is_base` value, collapsing every environment's entries
onto one name.
conda lays named environments out as `<root>/envs/<name>`, which never names
a base prefix, so `_is_base_prefix` treats that layout as authoritative and
falls back to comparing against `base_prefix` otherwise. `Menu` caches the
answer as `is_base_environment` so both decision sites share it.
<details><summary>Claude's draft</summary> Two hardening changes to _is_base_prefix. Require conda-meta above the envs directory. `<root>/envs/<name>` is only meaningful when `<root>` is itself a conda prefix, and every conda prefix carries `conda-meta`. Checking for it removes the false positive the PR description called out as its cost: a genuine base installation at a path like /opt/envs/mydist is now still reported as base, because /opt is not a conda prefix. The rule still fires for the case it is meant to catch, where `<root>` is a real installation. Do not let the comparison raise. `Path.samefile` requires both paths to exist. menuinst is handed prefixes that may already be gone -- removing shortcuts for a prefix being torn down -- and a missing path there is a FileNotFoundError out of `Menu.__init__`, not a fallback. `_same_path` tries `os.path.samefile` and falls back to comparing normalized absolute paths, which also makes the comparison correct on case-insensitive filesystems. The test fixture now builds a realistic root (`conda-meta` in the root and in the environment), plus two cases: an `envs` directory with no conda prefix above it, and prefixes that do not exist. Resume this Claude session: ``` cd /home/mark/git/feedstock/menuinst-src claude --resume b888d0ac-7cec-4e1e-ac68-9c15e6554011 ``` </details> Claude-Session: https://claude.ai/code/session_0135Eijr6BTzjVHcRcn3JP8w
hmaarrfk
added a commit
to hmaarrfk/menuinst-feedstock
that referenced
this pull request
Aug 23, 2026
<details><summary>Claude's draft</summary> Two changes to 538.patch, matching what was pushed to conda/menuinst#538: Require conda-meta above the envs directory. `<root>/envs/<name>` only means "named environment" when `<root>` is itself a conda prefix, and every conda prefix carries conda-meta. Checking for it removes the false positive the original heuristic accepted: a genuine base installation at a path like /opt/envs/mydist is still reported as base, because /opt is not a conda prefix. Do not let the comparison raise. Path.samefile requires both paths to exist, so a prefix that has already been removed -- deleting shortcuts for a prefix being torn down -- raised FileNotFoundError out of Menu.__init__ instead of falling back. _same_path tries os.path.samefile and falls back to comparing normalized absolute paths, which also makes the comparison correct on case-insensitive filesystems. 334.patch's three `self.menu.prefix.samefile(self.menu.base_prefix)` call sites in linux.py now use `menu.is_base_environment`, so the target_environment_is_base dispatch agrees with the one in base.py. Without this they kept the old comparison and took the is_base branch in every environment, which is the bug 538 is fixing. Windows builds are restricted to Python 3.12 and 3.14. Rendered matrix is linux_64_ (noarch), win_64 3.12, win_64 3.14 and win_arm64 -- four jobs instead of seven. Build 205 rather than reusing 204, since 204 was already built in CI from the previous 538.patch and the two are not the same package. Verified on a pristine 2.5.2 tarball: all six patches apply in order under both `patch -p1` and `git apply -p1`, upstream suite is 79 passed / 35 skipped, and installing a menu item from a conda-in-an-environment produces `Name=MyApp (myenv)` with no run_in_bash key in the .desktop file. Resume this Claude session: ``` cd /home/mark/git/feedstock/menuinst-feedstock claude --resume b888d0ac-7cec-4e1e-ac68-9c15e6554011 ``` </details> Claude-Session: https://claude.ai/code/session_0135Eijr6BTzjVHcRcn3JP8w
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I'm not yet convinced about this strategy, but I often install conda in my working environments since managing the base prefix's conda can get tiring.
Draft, because the "should an explicit
--root-prefixwin?" question below deserves a maintainer opinion.Description
menuinst decides whether the target prefix is the base environment by comparing it against
base_prefix:https://github.com/conda/menuinst/blob/main/menuinst/platforms/base.py#L44
and again in
MenuItem.__init__for the base/non-basenameobject.base_prefixultimately comes frommenuinst.utils.DEFAULT_BASE_PREFIX, which follows the running interpreter. Installing conda into an environment is legitimate and common, and such a conda reports that environment as its own base:So anything driving menuinst from that environment — including conda itself, when it is the conda in that environment — hands menuinst the environment as both the target prefix and the base prefix, and menuinst concludes it is base. On
main:The target prefix is
<root>/envs/myenv, so both answers are wrong.{{ ENV_NAME }}renders asbase, and thenameobject resolves to itstarget_environment_is_basevalue — which loses exactly the distinction that object exists to provide. Install the same package into several environments from such a conda and every environment's entries collide under one name.The patch
conda lays named environments out as
<root>/envs/<name>, and never puts a base prefix there.<root>is itself a conda prefix, so it carriesconda-meta; requiring that keeps the rule from firing on an installation that merely happens to sit under a directory calledenvs._is_base_prefixtakes that layout as authoritative and falls back to comparing againstbase_prefixotherwise:_same_pathreplaces the barePath.samefile, which requires both paths to exist and raises otherwise. menuinst is handed prefixes that may already be gone — removing shortcuts for a prefix being torn down — and today that is aFileNotFoundErrorout ofMenu.__init__rather than a fallback. It also makes the comparison correct on case-insensitive filesystems:Menucaches the answer asis_base_environmentso the two decision sites share one definition instead of repeating the comparison.With the patch, the reproduction above prints
env_name = myenvand picks thetarget_environment_is_not_basevalue.Should an explicit
--root-prefixstill win?I chose no — the
envslayout wins even whenbase_prefixwas passed explicitly. My reasoning, and I am happy to be argued out of it:base_prefixcan only disagree in one situation:prefix == base_prefixand that path sits under a directory namedenvsand the directory above it is a conda prefix. Every other combination — including the ordinary--root-prefix <root> --prefix <root>/envs/myenv— already answers "not base" and is unchanged.<root>/envs/myenvis a base installation.envs, so the rule costs nothing for installations laid out the normal way.The
conda-metacheck narrows this further. A base installation that genuinely lives at a path like/opt/envs/mydistis still reported as base, because/optis not a conda prefix. What remains is the case where someone deliberately treats<root>/envs/<name>of a real conda installation as a base prefix in its own right, which is the case this PR is arguing is a misreport rather than an intent. If you would rather havebase_prefixwin whenever it was explicitly supplied, that needs a way to distinguish "explicitly passed" from "defaulted", which today's signature does not have — I would rather add that than guess.Scope: what I deliberately did not change
record_shortcutsinapi.pyalso comparesprefix.samefile(base_prefix), to decide wheredistribution_namegets written. That one is not asking "is this the base environment?" — it is pairing with_get_distribution_name, which reads$BASE_PREFIX/Menu/menuinst.toml. The write and the read have to agree on the same path, so changing only the write would strand the value. Left alone._get_distribution_name'sbase_prefix.namefallback has the same root cause: under a conda-in-an-environment it yields the environment's name as the distribution name. Fixing that needs the real root, which menuinst does not have here. Out of scope for this PR; worth its own issue if you want it tracked.envs_dirs. conda can be configured to create named environments somewhere that is not calledenvs, and this heuristic will not fire there. It is a safety net for the default layout, not a complete answer.Testing
New
tests/test_menu_prefixes.py, which drivesMenu/MenuItemdirectly and runs on every platform:is_base_environment,env_name == "base", name resolves to the base valueprefix=<root>/envs/myenv,base_prefix=<root>→ not base (regression guard; this already worked)prefix == base_prefix == <root>/envs/myenv→ not base — the bugprefix == base_prefixoutside anyenvsdirectory → still base, so the heuristic does not over-fireprefix == base_prefixunder anenvsdirectory with no conda prefix above it → still base, so theconda-metacorroboration does its jobFileNotFoundErrorThe new tests all reference
is_base_environment, which does not exist onmain, so "run them onmain" only producesAttributeError. To show the behaviour genuinely changes I ran the assertions that do not need the new attribute against unpatched code:Full suite on macOS (Python 3.12):
77 passed, 27 skipped, 1 failed. The failure istests/test_elevation.py::test_elevation, which fails identically on unmodifiedmainin this environment and is unrelated.pre-commit run --all-filespasses (all 19 hooks).What I could not test. No Linux or Windows desktop session here, so the
PLATFORM-gated andCI-gated integration tests did not run. I also could not exercise the Windows side of this, where the same base/non-base branch feeds shortcut naming. The reproduction above was run against a real conda installed into a real environment, but on macOS only.Checklist - did you ...
newsdirectory (using the template) for the next release's release notes?target_environment_is_baseif you are happy with it.