Skip to content

Expand the ability to specify parameters by is_base or not - #334

Draft
hmaarrfk wants to merge 4 commits into
conda:mainfrom
hmaarrfk:expand_more_things_as_dict
Draft

Expand the ability to specify parameters by is_base or not#334
hmaarrfk wants to merge 4 commits into
conda:mainfrom
hmaarrfk:expand_more_things_as_dict

Conversation

@hmaarrfk

@hmaarrfk hmaarrfk commented May 17, 2025

Copy link
Copy Markdown
Contributor

In my workflow, I change a bit more than just the name depending on the environment being base or not.
This expands the functionality of #180
xref: #207

This provides those switches.

It also provides a way to "not run inside a bash shell"

Let me know if you are interested in this, I need to go through the checklist.

Checklist - did you ...

  • Add a file to the news directory (using the template) for the next release's release notes?
  • Add / update necessary tests?
  • Add / update outdated documentation?

@github-project-automation github-project-automation Bot moved this to 🆕 New in 🔎 Review May 17, 2025
@conda-bot conda-bot added the cla-signed [bot] added once the contributor has signed the CLA label May 17, 2025
@hmaarrfk
hmaarrfk force-pushed the expand_more_things_as_dict branch 2 times, most recently from 9514862 to b36bf87 Compare May 17, 2025 21:23
@jaimergp

Copy link
Copy Markdown
Member

Hm, I'm not sure about this. The is_base bits were implemented to maintain feature parity with menuinst 1.x, but I was never a fan. Assuming you control the installer generation via constructor, if you need different commands per environment, can that be achieved with two different packages: shortcut-for-app and shortcut-for-app-in-base?

@hmaarrfk

Copy link
Copy Markdown
Contributor Author

Your suggestion would work, and it isn’t a bad one. I might just do it.

The problem is that any application will have duplicate their menuinst packages. That’s fine for me. But will generate future annoyance for users.

I’ll try and see how it looks.

Thanks again for the reviews!

@jaimergp

jaimergp commented Jul 7, 2025

Copy link
Copy Markdown
Member

In the future, base is going to be a protected environment and apps will be recommended to ship a "default" env with their software, leaving conda in base (and nothing else), as drafted in conda-forge/miniforge#740. I anticipate those keys to be increasingly infrequent 🤞

@hmaarrfk

hmaarrfk commented Jul 7, 2025

Copy link
Copy Markdown
Contributor Author

But in my use case, my main app is « my application » and not conda. I want my base to be protected too ;)

this is less for miniforge, more for customized use cases.

@jaimergp

jaimergp commented Jul 7, 2025

Copy link
Copy Markdown
Member

Fair enough. Also, I think you could accomplish the same with a single package, but two build variants, with some sed replacement similar to what we do in the napari shortcut.

@hmaarrfk

Copy link
Copy Markdown
Contributor Author

I've held off on commenting, but 1 year later i find this inconsistency to still be thorn in our setup process.

but two build variants, with some sed replacement

is pretty complicated. variants and keeping them in sync is a chore.

Would you reconsider these additions?

@marcoesters

Copy link
Copy Markdown
Contributor

The dict structure has a very big drawback that I hadn't considered back then: conda-standalone comes with frozen menuinst versions, so new shortcuts that rely on an updated menuinst will fail to uninstall in the end. So, if you update menuinst in the base environment and install shortcuts with the new dictionary structure in the schema, uninstalling with the uninstaller will fail.

#477 may provide a way out though: I made a suggestion to enable the menuinst CLI to dynamically set variables at install time that will be respected by both the installation and uninstallation. This could be expanded to arbitrary placeholders, now that I think about it. Would that potentially be an alternative and more robust solution for you?

I would also move the bash details into a separate PR because it's a separate concern.

@hmaarrfk

Copy link
Copy Markdown
Contributor Author

#477 may provide a way out though: I made a suggestion to enable the menuinst CLI to dynamically set variables at install time that will be respected by both the installation and uninstallation. This could be expanded to arbitrary placeholders, now that I think about it. Would that potentially be an alternative and more robust solution for you?

can i circle back to this after #477 is merged? it seems like a lot of changes, so i'm hopeful I'll be able to use it.

@marcoesters marcoesters mentioned this pull request May 14, 2026
3 tasks
@lrandersson

Copy link
Copy Markdown
Contributor

Hi @hmaarrfk could you provide more with more context to your use-cases?

@hmaarrfk

Copy link
Copy Markdown
Contributor Author

Hi @hmaarrfk could you provide more with more context to your use-cases?

Lets consider installing Spyder. And I know that Spyder can be installed in one environment, then USE the python of an other, but I've never found that to be hyper reliable.

There is one usecase where you want to install spyder in 2 environments that are NOT base:

  • Spyder in environment dev1
  • Spyder in environment dev2

In this case, you want Spyder to show up on linux as Spyder (dev1) and Spyder (dev2) since you are a developer and understand the difference between environments.

In the case that you now wnat to ship Spyder, as a standalone application

  • Spyder will be created in constructor in the "base" environment.

You don't want Spyder (base) to appear, That doesn't make much sense to the end users. You want just Spyder.

That is the main usecase and the logic I want.

I want to design different names for my application when it is installed in base (it can assume it is a standalone application), and when it is in an environment (where the (env_name) should appear to help distinguish it).

There are many "customizations" accross most of the options in menuinst that need to happen.

I would prefer to not have 2 packages to install.

@hmaarrfk

Copy link
Copy Markdown
Contributor Author

Thanks for working to learn more about my usecase!

@lrandersson lrandersson self-assigned this May 18, 2026
@lrandersson

lrandersson commented May 19, 2026

Copy link
Copy Markdown
Contributor

Hi @hmaarrfk thanks for sharing this. If the primary use case is just the name (Spyder vs Spyder (env)) I'd say that adding built-in placeholders would be the easiest. I.e.:

  • Add ENV_NAME_IF_NOT_BASE placeholder that resolves to (env_name) or an empty string.
  • Package authors write: "name": "Spyder{{ ENV_NAME_IF_NOT_BASE }}".
  • Advantage of this is: No schema change, backward compatible, solves the common use case you have mentioned.
  • Downside with this approach is that it is limited to specific patterns, can't cover all use cases in this PR (different commands, etc.)

Other ways forward could be:

  1. Add IS_BASE as a built-in placeholder, but I think it would be clunky and you'd need two placeholders like IS_BASE_SUFFIX that resolve differently
  2. Support this PRs dict structure BUT store the resolved value in menuinst.toml
    • At install time, menuinst resolves the dict and stores the chosen value in menuinst.toml
    • At uninstall time, menuinst reads from menuinst.toml (even if frozen menuinst can't parse the original JSON)
    • I think this would give the most flexibility for package authors, and solves the conda-standalone problem
    • It's a more complex implementation.

@hmaarrfk

Copy link
Copy Markdown
Contributor Author

Unfortunately for me. A partial solution just isn’t enough.

I might take Jaime’s suggestion and just deal with two helper package names. This is really not ideal since I feel like what I’m asking for is a natural extension of the schema that is already here.

@hmaarrfk
hmaarrfk marked this pull request as ready for review August 23, 2026 01:23
@hmaarrfk
hmaarrfk requested a review from a team as a code owner August 23, 2026 01:23
@hmaarrfk

Copy link
Copy Markdown
Contributor Author

one year later, i just feel like this is a reasonable request.

you may disagree, but please take this as empowering a user of yours to avoid making a fork for something that the original creators of Anaconda/conda felt was a gap too:

For the "base environment" there is something special to consider.

Feel free to close this at this point, but I think this is a minor expansion of the scope, that has the capabilities to really help maintain one of your users.

@hmaarrfk
hmaarrfk force-pushed the expand_more_things_as_dict branch from b36bf87 to a92e275 Compare August 23, 2026 01:27
hmaarrfk added a commit to hmaarrfk/menuinst-feedstock that referenced this pull request Aug 23, 2026
<details><summary>Claude's draft</summary>

Refresh this staging branch onto the current feedstock main (menuinst
2.5.2, rattler-build v1 recipe) and carry all four of my open
conda/menuinst pull requests as source patches:

- 333.patch — conda/menuinst#333
  Expand placeholders in lists of strings, not just scalars.
- 334.patch — conda/menuinst#334
  Allow name/command/StartupWMClass/TryExec to be specified per
  target_environment_is_base, and add the linux `run_in_bash` key.
  Rebased onto upstream main: `min_items` -> `min_length` for pydantic
  v2, `Optional[A, B]` -> `Optional[Union[A, B]]`, and SCHEMA_VERSION
  bumped to 1-1-4 with the generated schema/default JSON regenerated so
  `run_in_bash` has a default to resolve against.
- 535.patch — conda/menuinst#535
  Restore the missing f-string prefix on the Icon= desktop entry line.
- 536.patch — conda/menuinst#536
  Pass the opened file or URL to Linux shortcuts via an Exec field code.

334 and 536 both rewrite LinuxMenuItem._command; 536.patch carries the
merged form, which keeps the field-code "$@" trick for the bash-wrapped
case and appends the field code directly when run_in_bash is false.

Build number bumped by 100 and the build restricted to linux-64, since
this is a staging branch for my own channel.

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
hmaarrfk added a commit to hmaarrfk/menuinst-feedstock that referenced this pull request Aug 23, 2026
<details><summary>Claude's draft</summary>

Refresh this staging branch onto the current feedstock main (menuinst
2.5.2, rattler-build v1 recipe) and carry all four of my open
conda/menuinst pull requests as source patches:

- 333.patch — conda/menuinst#333
  Expand placeholders in lists of strings, not just scalars.
- 334.patch — conda/menuinst#334
  Allow name/command/StartupWMClass/TryExec to be specified per
  target_environment_is_base, and add the linux `run_in_bash` key.
  Rebased onto upstream main: `min_items` -> `min_length` for pydantic
  v2, `Optional[A, B]` -> `Optional[Union[A, B]]`, and SCHEMA_VERSION
  bumped to 1-1-4 with the generated schema/default JSON regenerated so
  `run_in_bash` has a default to resolve against.
- 535.patch — conda/menuinst#535
  Restore the missing f-string prefix on the Icon= desktop entry line.
- 536.patch — conda/menuinst#536
  Pass the opened file or URL to Linux shortcuts via an Exec field code.

334 and 536 both rewrite LinuxMenuItem._command; 536.patch carries the
merged form, which keeps the field-code "$@" trick for the bash-wrapped
case and appends the field code directly when run_in_bash is false.

Build number bumped by 100 so this can be uploaded to my own channel.
All platforms stay enabled.

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
hmaarrfk added a commit to hmaarrfk/menuinst-feedstock that referenced this pull request Aug 23, 2026
<details><summary>Claude's draft</summary>

Rebased onto the noarch-except-windows branch (conda-forge#67) so this staging
build produces one noarch package plus the Windows builds.

Carry all four of my open conda/menuinst pull requests as source
patches:

- 333.patch — conda/menuinst#333
  Expand placeholders in lists of strings, not just scalars.
- 334.patch — conda/menuinst#334
  Allow name/command/StartupWMClass/TryExec to be specified per
  target_environment_is_base, and add the linux `run_in_bash` key.
  Rebased onto upstream main: `min_items` -> `min_length` for pydantic
  v2, `Optional[A, B]` -> `Optional[Union[A, B]]`, and SCHEMA_VERSION
  bumped to 1-1-4 with the generated schema/default JSON regenerated so
  `run_in_bash` has a default to resolve against.
- 535.patch — conda/menuinst#535
  Restore the missing f-string prefix on the Icon= desktop entry line.
- 536.patch — conda/menuinst#536
  Pass the opened file or URL to Linux shortcuts via an Exec field code.

334 and 536 both rewrite LinuxMenuItem._command; 536.patch carries the
merged form, which keeps the field-code "$@" trick for the bash-wrapped
case and appends the field code directly when run_in_bash is false.

Build number bumped by 100 over the base so this can be uploaded to my
own channel.

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
@hmaarrfk

Copy link
Copy Markdown
Contributor Author

and i'll put conda-forge/menuinst-feedstock#67 up as a token of me being a good "steward" of open source.

Comment thread menuinst/_schema.py Outdated
Comment thread menuinst/_schema.py Outdated
"""
),
)
run_in_bash: Optional[bool] = Field(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one feels like it belongs in a different PR.

@jaimergp jaimergp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the criteria for adding the base-or-not dict to some fields but not others, simple necessity as of now? What's the update story for the inevitable moment when someone wants to add them to more places? Will we have more variables in this hardcoded conditional at some point?

If we accept to do this, I see ourselves going into the direction of generalised recipe.yaml-style if: / then: dictionaries, with some predefined runtime variables, and then accept those anywhere not just in a perhaps-arbitrary fashion.

That aside, we'll need to:

  • Add tests and examples
  • Update documentation
  • Add news

@github-project-automation github-project-automation Bot moved this from 🆕 New to 🏗️ In Progress in 🔎 Review Aug 25, 2026
@hmaarrfk
hmaarrfk force-pushed the expand_more_things_as_dict branch from 37ca451 to e4041bd Compare August 25, 2026 11:30
@hmaarrfk
hmaarrfk force-pushed the expand_more_things_as_dict branch from e4041bd to e28a5a1 Compare August 25, 2026 11:30
@hmaarrfk

Copy link
Copy Markdown
Contributor Author

What is the criteria for adding the base-or-not dict to some fields

necessity is pretty close to my decision making process.

Do note that this whole "I install things in my own conda environment inside a whole operating system) is somewhat new.

These things also end up being somewhat "public" at the interface between different subsystems that try to help with identification (by humans or other code)

  • name: I want to be able to toggle more information for developers than for non-developers (who would install in base)
  • command: depending on how WMClass gets set, it may be necessary to change this based on the same criteria (perhaps you add a flag like --wmclass).
  • StartupWMClass: I really want to be able to set one of my environments as the "default" and so differentiating between the windwos of the different environments is critical, and this is thus needed.
  • TryExec: as command

So if something doesn't end in (env_name), likewise it would make sense if the Exec and other thigns didn't have -base at the end of them. For symmetry

If we accept to do this, I see ourselves going into the direction of generalised recipe.yaml-style if: / then: dictionaries, with some predefined runtime variables, and then accept those anywhere not just in a perhaps-arbitrary fashion.

Understood. I don't see this as a problem. We have all been working toward better system integration. I see the present solution as a step in enabling that.

That aside, we'll need to:

Add tests and examples
Update documentation
Add news

I will ping again when this is ready.

@hmaarrfk
hmaarrfk marked this pull request as draft August 25, 2026 11:36
@hmaarrfk
hmaarrfk force-pushed the expand_more_things_as_dict branch from 7e36725 to 1fe5291 Compare August 25, 2026 14:15
@jaimergp

Copy link
Copy Markdown
Member

Understood. I don't see this as a problem. We have all been working toward better system integration. I see the present solution as a step in enabling that.

Agreed, but I'd like to avoid the intermediate steps so we don't have to support too many different schemas, although this particular one may not be a big burden.

<details><summary>Claude's draft</summary>

Three unrelated breakages were making every CI job red:

* `SCHEMA_VERSION` said `1-1-4` in `menuinst/_schema.py` and
  `menuinst/platforms/base.py`, but the generated data files were named
  `menuinst-1-2-0.*` — a stale-rebase artifact. Importing `menuinst` at
  all raised `FileNotFoundError` on the missing
  `menuinst-1-1-4.default.json`, which took down the test suite on every
  platform (conftest import error) and the docs build (autodoc import
  error). Settled on `1-2-0` as the new schema version and regenerated
  both data files against it. Widening `command` from an array to
  array-or-object keeps every existing document valid, but it can break
  consumers that assume `command` is a list, so this is a SchemaVer
  REVISION rather than an ADDITION.

* `Remove run_in_bash` (e28a5a1) deleted the option but kept the
  `run_in_bash: false` branch, so `LinuxMenuItem._command` stopped
  wrapping the command in `bash -c`. That is a behavior change for
  every Linux shortcut, not a revert. Restored the `main` behavior,
  which also makes the now-unused `shlex` import used again and
  unbreaks the ruff pre-commit hook.

* `docs/source/reference.md` still pointed `autopydantic_model` at
  `menuinst._schema.MenuItemNameDict`, renamed on this branch. Docs
  are built with `-W`, so the dangling reference was fatal. Pointed
  it at `TargetIsBaseConStr` and added `TargetIsBaseConList`.

Verified locally: `pytest tests/` (only the two pre-existing
macOS-local failures, `test_osx_symlinks` and `test_elevation`, which
fail on `main` too), `pre-commit run --all-files`, and
`make dirhtml SPHINXOPTS="-W --keep-going -n"`.

Resume this Claude session:
```
cd /Users/mark/git/menuinst
claude --resume 08aef327-5520-4aa8-a267-4c3f0597d871
```
</details>
<details><summary>Claude's draft</summary>

Addresses the three follow-up bullets from review 5017801096.

**Tests and examples**

* New example `tests/data/jsons/target-environment.json` exercising every
  field that accepts the base-or-not form at once, with distinguishable
  branches. It is picked up automatically by `test_examples`, so the schema
  itself is covered.
* `test_target_environment_dictionary` checks `name`, `command` and
  `precommand` resolve to the right branch on all platforms, including the
  case where a `platforms.win` block overrides a top-level value with
  another base-or-not dictionary.
* `test_target_environment_desktop_entry` (Linux) checks
  `StartupWMClass`/`TryExec`/`Name`/`Exec` in the written `.desktop` file,
  and that placeholders are expanded *after* the branch is picked.
* `test_target_environment_missing_branch` covers the error path.
* `test_target_environment_leaves_other_dicts_alone` pins the resolver to
  mappings actually shaped like a branch pair, so `glob_patterns` and
  friends pass through untouched.

**Code needed to make those tests pass**

The schema declared `command` as base-or-not for every platform, but only
`LinuxMenuItem` knew how to resolve it: a dictionary `command` on macOS or
Windows reached the argument quoting as a `dict`. Conversely `precommand`
was resolved in `linux.py` but not declared in the schema, so it could
never validate. Both are fixed by resolving the branch once, in
`MenuItem.__init__`, where `name` was already being handled:
`_resolve_target_environment_keys` walks the flattened metadata and
collapses any value shaped like a branch pair. The three ad-hoc branches in
`linux.py` go away, and `precommand` is declared as accepting
`TargetIsBaseConStr`. Adding a field to the set is now a schema-only
change.

**Documentation**

`defining-shortcuts.md` grows a `(base-or-not)` section that states the
rule once, tables the fields that accept it, and adds a `command` /
`TryExec` example next to the existing `name` one. Field descriptions in
`_schema.py` now say which fields accept the form (these are what the
reference page renders), and the `TargetIsBaseConStr`/`TargetIsBaseConList`
docstrings describe the models rather than repeating "Name when ...".

**News**

`news/334-target-environment-dicts`.

Verified: `pytest tests/` on macOS and in a `condaforge/miniforge3`
container (only the failures that also fail on `main` in each environment
remain), `pre-commit run --all-files`, and
`make dirhtml SPHINXOPTS="-W --keep-going -n"`.

Not addressed here: the reviewer's opening question about the criteria for
the field set and the eventual `if:/then:` generalisation. The docs now
state the set is deliberately fixed and point at an issue, but that is a
design decision for the maintainers, not something to settle in this PR.

Resume this Claude session:
```
cd /Users/mark/git/menuinst
claude --resume 08aef327-5520-4aa8-a267-4c3f0597d871
```
</details>
@hmaarrfk
hmaarrfk force-pushed the expand_more_things_as_dict branch from 1fe5291 to 3e6708f Compare August 25, 2026 19:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed [bot] added once the contributor has signed the CLA

Projects

Status: 🏗️ In Progress

Development

Successfully merging this pull request may close these issues.

5 participants