Open mrclary opened 5 months ago
Hm, can you try with a leading period for the extensions? See example.
"It works on CI", but CI runs as administrator by default, so we might need to adjust something. "Open With" has not been added per se, as it requires some extra registry keys.
@mrclary did you have a chance to take a look at the workaround?
@jaimergp, sorry, I have not yet had a chance to look at it.
I suspect that an error occurs on install after the first item in the file extension list is handled resulting in no other extensions being handled, but the shortcut is produced. On uninstall, an error results due to the missing expected AssocFiles.
So I tried using menuinst
in a standard conda environment.
(base) C:\Users\rclary>mamba create -n spyder6_test -c conda-forge/label/spyder_dev -c conda-forge/label/spyder_kernels_rc spyder=6.0.0a4
This version of Spyder does not have file extensions listed in the spyder-menu.json
file and the shortcut is created without issue. I can also uninstall it without issue.
(base) C:\Users\rclary>set bp=C:\Users\AppData\Local\miniforge3
(base) C:\Users\rclary>set tp=%bp%\envs\spyder6_test
(base) C:\Users\rclary>set menu=%tp%\Menu\spyder-menu.json
(base) C:\Users\rclary>python -c "from menuinst.api import remove; remove(r'%menu%', target_prefix=r'%tp%', base_prefix=r'%bp%')"
However, I get the following errors after modifying spyder-menu.json
to include two file extensions.
(base) C:\Users\rclary>python -c "from menuinst.api import install; install(r'%menu%', target_prefix=r'%tp%', base_prefix=r'%bp%')"
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\utils.py", line 426, in wrapper_elevate
return func(
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\api.py", line 62, in install
paths += menu_item.create()
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\platforms\win.py", line 167, in create
self._register_file_extensions()
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\platforms\win.py", line 365, in _register_file_extensions
register_file_extension(ext, identifier, command, icon=icon, mode=self.menu.mode)
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\platforms\win_utils\registry.py", line 75, in register_file_extension
winreg.SetValueEx(subkey, "DefaultIcon", 0, winreg.REG_SZ, icon)
PermissionError: [WinError 5] Access is denied
HKCU\Software\Classes\spyder-6-spyder6-test.AssocFile.py
appears in the registry, but not HKCU\Software\Classes\spyder-6-spyder6-test.AssocFile.pyw
.
And attempting to uninstall:
(base) C:\Users\rclary>python -c "from menuinst.api import remove; remove(r'%menu%', target_prefix=r'%tp%', base_prefix=r'%bp%')"
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\utils.py", line 426, in wrapper_elevate
return func(
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\api.py", line 84, in remove
paths += menu_item.remove()
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\platforms\win.py", line 173, in remove
self._unregister_file_extensions()
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\platforms\win.py", line 375, in _unregister_file_extensions
unregister_file_extension(ext, identifier, mode=self.menu.mode)
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\platforms\win_utils\registry.py", line 88, in unregister_file_extension
_reg_exe("delete", fr"{root_str}\Software\Classes\{identifier}")
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\platforms\win_utils\registry.py", line 25, in _reg_exe
return logged_run(["reg.exe", *args, "/f"], check=True)
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\utils.py", line 470, in logged_run
process.check_returncode()
File "C:\Users\rclary\AppData\Local\miniforge3\lib\subprocess.py", line 457, in check_returncode
raise CalledProcessError(self.returncode, self.args, self.stdout,
subprocess.CalledProcessError: Command '['reg.exe', 'delete', 'HKCU\\Software\\Classes\\spyder-6-spyder6-test.AssocFile.pyw', '/f']' returned non-zero exit status 1.
HKCU\Software\Classes\spyder-6-spyder6-test.AssocFile.py
is removed from the registry, but there is the error for .pyw
.
If I only have one extension in the list, the install produces the same error, although the AssocFile is produced. The uninstall does not produce an error. So, I suspect that the error occurs on install after the first extension in the list is handled resulting in no other extensions being handled, but the shortcut is produced nonetheless. Then, on uninstall, the error is a result of the missing expected AssocFiles.
Looks like the problem is at:
So maybe it works if you remove the icon
entry from the JSON. I'll check where that permissions issue is coming from and add tests with icons. I see several things to fix the code with:
@jaimergp, with menuinst=2.1.1
I no longer have the issues I described in my previous comment: https://github.com/conda/menuinst/issues/185#issuecomment-1982536505 👏🏼. I can add/remove the shortcut without errors and the registry entries are added/removed as expected.
The only issue that remains is that the file type association in context menus appears as Python instead of Spyder (both name and icon). Nevertheless, associating with "Python" does, in fact, open the file in Spyder. Perhaps this is a result of the shortcut target using pythonw.exe
instead of spyder.exe
? I know we've discussed in the past regarding executable targets causing cmd windows to open, which is why I target pythonw.exe
instead.
@jaimergp, actually there is still an issue with the registry when removing an environment. For example
(base) C:\Users\rclary>mamba remove -n spy6b3d8 --all
Remove all packages in environment C:\Users\rclary\.conda\envs\spy6b3d8:
Preparing transaction: done
Verifying transaction: done
Executing transaction: | menuinst Exception
Traceback (most recent call last):
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\conda\gateways\disk\create.py", line 261, in make_menu
menuinst.install(
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\api.py", line 168, in _install_adapter
_api_remove(metadata, target_prefix=prefix, **kwargs)
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\utils.py", line 426, in wrapper_elevate
return func(
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\api.py", line 84, in remove
paths += menu_item.remove()
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\platforms\win.py", line 207, in remove
self._unregister_file_extensions()
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\platforms\win.py", line 457, in _unregister_file_extensions
unregister_file_extension(ext, identifier, mode=self.menu.mode)
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\platforms\win_utils\registry.py", line 89, in unregister_file_extension
_reg_exe("delete", fr"{root_str}\Software\Classes\{identifier}")
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\platforms\win_utils\registry.py", line 26, in _reg_exe
return logged_run(["reg.exe", *args, "/f"], check=True)
File "C:\Users\rclary\AppData\Local\miniforge3\lib\site-packages\menuinst\utils.py", line 470, in logged_run
process.check_returncode()
File "C:\Users\rclary\AppData\Local\miniforge3\lib\subprocess.py", line 457, in check_returncode
raise CalledProcessError(self.returncode, self.args, self.stdout,
subprocess.CalledProcessError: Command '['reg.exe', 'delete', 'HKCU\\Software\\Classes\\spyder-6-spy6b3d8.AssocFile.bat', '/f']' returned non-zero exit status done
The registry key is removed, so perhaps it is trying to delete it again and it's no longer there?
Also, FYI, conda env remove -n spy6b3d8
removes the environment, but not the shortcut or registry keys and there is no error reported. Only conda remove -n spy6b3d8 --all
removes the menuinst
shortcut, registry keys, and the environment, but with the reported error.
Also, FYI,
conda env remove -n spy6b3d8
removes the environment, but not the shortcut or registry keys and there is no error reported.
Yes, this is a known issue: https://github.com/conda/conda/issues/11092.
I'll look into the other reports, thanks for giving it a try!
Edit: Found these resources. Writing them down here for future reference:
,index
syntax). I'm not sure how we can add a custom text resource without compiling a custom DLL yet.Apparently Windows wants us to use "indirect strings" for those Progids attributes. We can't just put the text directly there.
Instead, we are asked to bundle the custom strings in a resource binary (.rsrc
section in a PE file). These files are usually written by passing an XML file to rc.exe
(part of the Windows SDK). We can't depend on that or expect it to be installed (too big, compiled, non-redistributable).
Writing a PE file from scratch is technically possible with pefile
, pe-tools
or lief
; the former is preferred because it's lighter, but it will take a lot of experimentation to find out how the strings look like in the final file. lief
has some resource handling convenience wrappers that might make it easier but it's heavier as a dependency that will be added to conda
...
Huh, turns out at least one more person in the world has looked into this and reported that Windows 10 does support raw strings without the PE indirection. That simplifies things drastically. I'll open a PR once I can push to the repo.
@mrclary, is there a chance you can try https://github.com/conda/menuinst/pull/225 locally?
Checklist
What happened?
@jaimergp, I get several errors when using
file_extensions
. Theconstructor
-based installer successfully installs the environment and shortcuts, however file types don't seem to be associated with Spyder, i.e. Spyder is not an available application to "open with" for expected file types. Additionally, attempting to uninstall or install withmenuinst
results in the following errors.Attempt to uninstall shortcut
```cmd [spyder](base) C:\Users\rclary>set bp=%LOCALAPPDATA%\spyder-6 [spyder](base) C:\Users\rclary>set tp=%bp%\envs\spyder-runtime [spyder](base) C:\Users\rclary>set menu=%tp%\Menu\spyder-menu.json [spyder](base) C:\Users\rclary>python -c "from menuinst.api import remove; remove(r'%menu%', target_prefix=r'%tp%', base_prefix=r'%bp%')" Traceback (most recent call last): File "Attempt to install shortcut
```cmd [spyder](base) C:\Users\rclary>python -c "from menuinst.api import install; Traceback (most recent call last): File "I get the same errors whether I run the installer as administrator or not, except the traceback file paths are
C:\ProgramData\spyder-6...
instead. The first error appears to result because thespyder-6.AssocFilec
registry key was not created as expected. I'm puzzled why the second error occurs even when run from a cmd window with administrator privileges: why wasn't aPermissionError
raised whenmenuinst
was run from the installer?.Conda Info
Conda Config
Conda list
Additional Context
spyder-menu.json
```json { "$schema": "https://json-schema.org/draft-07/schema", "$id": "https://schemas.conda.io/menuinst-1.schema.json", "menu_name": "{{ DISTRIBUTION_NAME }} spyder", "menu_items": [ { "name": "Spyder 6", "description": "Scientific PYthon Development EnviRonment", "icon": "{{ MENU_DIR }}/spyder.{{ ICON_EXT }}", "activate": false, "terminal": false, "command": [""], "platforms": { "win": { "desktop": true, "app_user_model_id": "spyder.Spyder", "command": ["{{ PREFIX }}/pythonw.exe", "{{ PREFIX }}/Scripts/spyder-script.py"], "file_extensions": [ "bat", "c", "cc", "cfg", "cl", "cmd", "cpp", "css", "cxx", "desktop", "diff", "enaml", "f", "f03", "f08", "f2k", "f77", "f90", "f95", "for", "h", "h", "hh", "hpp", "htm", "html", "hxx", "inf", "ini", "ipy", "ipynb", "jl", "js", "json", "m", "md", "nsh", "nsi", "patch", "po", "pot", "pro", "properties", "pxd", "pxi", "py", "pyw", "pyx", "reg", "rej", "rst", "scss", "session", "txt", "xml", "yaml", "yml" ] }, "linux": { "Categories": [ "Development", "Science" ], "command": ["{{ PREFIX }}/bin/spyder", "$@"], "StartupWMClass": "Spyder", "MimeType": [ "text/x-script.python", "text/plain", "text/html", "text/xml", "text/x-c", "text/x-perl" ] }, "osx": { "precommand": "pushd \"$(dirname \"$0\")\" &>/dev/null", "command": ["./python", "{{ PREFIX }}/bin/spyder", "$@"], "link_in_bundle": { "{{ PREFIX }}/bin/python": "{{ MENU_ITEM_LOCATION }}/Contents/MacOS/python" }, "CFBundleName": "Spyder 6", "CFBundleIdentifier": "org.spyder-ide.Spyder", "CFBundleVersion": "6.0.0a4.dev152", "CFBundleDocumentTypes": [ { "CFBundleTypeName": "text document", "CFBundleTypeRole": "Editor", "LSHandlerRank": "Default", "CFBundleTypeIconFile": "spyder.icns", "LSItemContentTypes": [ "com.apple.applescript.text", "com.apple.ascii-property-list", "com.apple.audio-unit-preset", "com.apple.binary-property-list", "com.apple.configprofile", "com.apple.crashreport", "com.apple.dashcode.css", "com.apple.dashcode.javascript", "com.apple.dashcode.json", "com.apple.dashcode.manifest", "com.apple.dt.document.ascii-property-list", "com.apple.dt.document.script-suite-property-list", "com.apple.dt.document.script-terminology-property-list", "com.apple.property-list", "com.apple.rez-source", "com.apple.scripting-definition", "com.apple.structured-text", "com.apple.traditional-mac-plain-text", "com.apple.xcode.ada-source", "com.apple.xcode.apinotes", "com.apple.xcode.bash-script", "com.apple.xcode.configsettings", "com.apple.xcode.csh-script", "com.apple.xcode.entitlements-property-list", "com.apple.xcode.fortran-source", "com.apple.xcode.glsl-source", "com.apple.xcode.ksh-script", "com.apple.xcode.lex-source", "com.apple.xcode.make-script", "com.apple.xcode.mig-source", "com.apple.xcode.pascal-source", "com.apple.xcode.strings-text", "com.apple.xcode.tcsh-script", "com.apple.xcode.yacc-source", "com.apple.xcode.zsh-script", "com.apple.xml-property-list", "com.netscape.javascript-source", "com.scenarist.closed-caption", "com.sun.java-source", "com.sun.java-web-start", "net.daringfireball.markdown", "org.khronos.glsl-source", "org.oasis-open.xliff", "public.ada-source", "public.assembly-source", "public.bash-script", "public.c-header", "public.c-plus-plus-header", "public.c-plus-plus-source", "public.c-source", "public.case-insensitive-text", "public.comma-separated-values-text", "public.csh-script", "public.css", "public.delimited-values-text", "public.dylan-source", "public.filename-extension", "public.fortran-77-source", "public.fortran-90-source", "public.fortran-95-source", "public.fortran-source", "public.html", "public.json", "public.ksh-script", "public.lex-source", "public.log", "public.m3u-playlist", "public.make-source", "public.mig-source", "public.mime-type", "public.module-map", "public.nasm-assembly-source", "public.objective-c-plus-plus-source", "public.objective-c-source", "public.opencl-source", "public.pascal-source", "public.patch-file", "public.perl-script", "public.php-script", "public.plain-text", "public.python-script", "public.rss", "public.ruby-script", "public.script", "public.shell-script", "public.source-code", "public.tcsh-script", "public.text", "public.utf16-external-plain-text", "public.utf16-plain-text", "public.utf8-plain-text", "public.utf8-tab-separated-values-text", "public.xhtml", "public.xml", "public.yacc-source", "public.yaml", "public.zsh-script" ] } ] } } } ] } ```