Pass the opened file or URL to Linux shortcuts via an Exec field code - #536
Pass the opened file or URL to Linux shortcuts via an Exec field code#536hmaarrfk wants to merge 3 commits into
Conversation
A menu item that declares `MimeType` registers as a handler for that type, but `Exec` carried no `%f`/`%u` field code, so a file manager had no way to tell menuinst which file was opened unless the item author threaded a field code through their own `command`. Auto-adds a field code when `MimeType` is declared and the command does not already contain one -- the same thing `WindowsMenuItem._process_command` does with `%1` when registering file extensions and URL protocols. `%u` is used when every declared MIME type is an `x-scheme-handler/` scheme, `%f` otherwise. `LinuxMenuItem._command` wraps the shortcut in `bash -c '<script>'`, and `bash -c` assigns its first operand to `$0`. A bare trailing field code would therefore be silently dropped, so the emitted form is `bash -c '<script> "$@"' bash %f`, with a literal `bash` occupying `$0`.
<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
<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
<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
marcoesters
left a comment
There was a problem hiding this comment.
I think that makes sense and achieves parity between Linux and the other platforms, thanks! I just have a few questions/comments.
If you have a few shortcuts to try, we can probably use a virtual machine to see that the code changes are doing what they're supposed to be doing, even though I like your test.
| if all(mime_type.startswith("x-scheme-handler/") for mime_type in mime_types): | ||
| return "%u" |
There was a problem hiding this comment.
I'm not super familiar with Linux shortcuts. Under which circumstances we have multiple MIME types where one is an x-scheme-handler/ and the other ones aren't? And why is it appropriate to default to %f under these circumstances?
There was a problem hiding this comment.
this is for url-like scehmes like
mailto://
or
sftp://
so you can register something like
conda-forge://
if you so wanted to...
in those cases the "uri" would be valid. otherwise you are declaring your application as needing a "file".
There was a problem hiding this comment.
What I'm getting at is that your code requires all MIME types to start with x-scheme-handler/, which suggests there may be multiple MIME types and that they may not all start with that string.
What is a scenario for having multiple MIME types where one is a URI and the other ones aren't, and why is it appropriate to fall back to a file rather than nothing or raising an exception?
|
Thank you for your thorough review, i will go through this shortly. |
Allow mime types to be auto-infered by filetype association
Claude summary and details
Draft, because the questions under "Open questions" below deserve a maintainer opinion before this is polished.
Description
A Linux menu item that declares
MimeTypegets registered as a handler for that type (_register_mime_typeswrites it intomimeapps.list), but the generatedExecline carries no field code. There is no%f,%F,%uor%Uanywhere inmenuinst/platforms/linux.py, at any version — 2.4.2 has none either, so this is long-standing rather than a regression.The consequence is that the file manager has nowhere to put the file: it launches the app with no argument.
The type does get registered, so the item shows up in "Open with" and is even installed as the default handler — it just cannot receive what the user opened.
There is a workaround today, and the test fixtures use it: the item author threads a field code through their own
command, astests/data/jsons/file_types.jsonandurl_protocols.jsondo. That works, but it puts a Linux-specific launcher detail into user metadata, and it is not what menuinst does on the other platforms.This is what menuinst already does on Windows
WindowsMenuItem._process_command(with_arg1=True)injects%1into the command, and_register_file_extensions/_register_url_protocolsboth call it that way:https://github.com/conda/menuinst/blob/main/menuinst/platforms/win.py#L376-L382
So auto-adding a field code on Linux when the item declares a
MimeTypeis the symmetric behaviour, not a new concept — including the "don't add it if the author already supplied one" guard, which this PR copies.The
bash -csubtletyLinuxMenuItem._commandwraps everything inbash -c '<script>'. A naively appended trailing%fdoes not work:bash -c SCRIPT NAME ARGS...assignsNAMEto$0, so the launcher's argument is swallowed.So the emitted form is
"$@"inside the quoted script plus a literalbashto occupy$0:What the patch does
_field_code()returns%uif every declared MIME type is anx-scheme-handler/…scheme,%fif any is a real file type, and""if there is noMimeTypeor if the renderedcommandalready contains a field code._command()takes an optionalfield_code; when it is empty the output is byte-for-byte what it was before.Open questions
I picked a default for each of these rather than block on them, and I am happy to change any of them:
%fvs%F. I chose singular%f/%u. They differ only for multi-file selection:%Fhands several paths to one process,%fmakes the launcher spawn one process per file. Singular is the conservative default for a wrappedbash -cscript that may not expect more than one argument, and it mirrors the singular%1used on Windows.%F/%Uwould be a one-line change if you prefer it.%ufor URL schemes.x-scheme-handler/…MIME types are how Linux expresses whatCFBundleURLTypesexpresses on macOS andurl_protocolson Windows, and the spec's%uis the URL form, so an item declaring only scheme handlers gets%u. An item declaring both gets%f; that mixed case is arguably better served by%u(which also accepts local paths) — I do not have a strong view.MimeTypevs an explicit schema key. Inferring needs no schema change and no action from existing recipes. An explicit key (sayplatforms.linux.field_code) would be more predictable and would let an author opt out, at the cost of a schema bump. Inference plus the "author already supplied one" escape hatch seemed like the better trade, but this is your call.Testing
New
tests/test_linux_menu_item.py, mirroring the existingtests/test_windows_menu_item.pyin structure. It drives_write_desktop_filedirectly, so it runs on every platform rather than needing a Linux desktop session:MimeType→ no field code%f;x-scheme-handler/…only →%u; mixed →%fcommand→ not duplicated, and the"$@"wrapper is not appliedExec,shlex.splitit, run it, and assert the command actually received the path. This is the test that fails if the$0detail above is got wrong.(The 2 that pass before are the negative controls, as expected.)
No existing shortcut changes. I rendered the
Execline for every JSON fixture intests/data/jsonsthat enables Linux, with and without the patch, and diffed:file_types.jsonandurl_protocols.jsonare the only fixtures that declareMimeType, and both already carry their own field code, so the guard keeps them untouched. The Linux CI cases for file-type and URL-protocol association should therefore be unaffected.Full suite on macOS (Python 3.12):
79 passed, 27 skipped, 1 failed. The one failure istests/test_elevation.py::test_elevation, which fails identically on unmodifiedmainin this environment and is unrelated to this change.pre-commit run --all-filespasses (all 19 hooks).What I could not test. I have no Linux desktop session here, so I could not run the
PLATFORM == "linux"/CItests —test_file_type_association,test_url_protocol_association,test_desktop_files_escaping— and I could not confirm against a real file manager that a double-click hands the path through as intended. The end-to-end test above emulates the launcher (substitute, split, execute) rather than being one. Linux CI on this PR is the real check.Checklist - did you ...
newsdirectory (using the template) for the next release's release notes?MimeType.