fix(downloads): use explorer /select on Windows + show open feedback
Reported: clicking "Open Downloads folder" did nothing visible. The
previous implementation called ``os.startfile(folder)`` on Windows,
which is known to silently no-op or open Explorer behind the active
window in some configurations (Streamlit running headless, no
foreground rights inherited by the click handler thread, etc.).
Switch to the more reliable ``explorer /select,<file>`` form:
- Opens Explorer with the just-saved file pre-highlighted instead of
just navigating to the folder — better UX than the old behavior.
- explorer.exe is a real GUI process that's spawned in the user's
session with foreground rights, so it shows up on top.
- Fallback chain on Windows: ``/select`` first, then plain
``explorer <folder>``, then ``os.startfile`` as a last resort.
macOS upgraded the same way: ``open -R <file>`` reveals in Finder
rather than opening the directory.
Linux: no reliable cross-distro reveal, so ``xdg-open <folder>``.
Plus user feedback at the call site:
- On successful dispatch: ``st.toast("Opening <folder>", icon="📂")``
— confirms we tried, in case the window comes up behind the
browser.
- On dispatch failure: ``st.warning`` with the full path the user
can copy/paste into their file manager manually.
2220 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -254,25 +254,60 @@ def _downloads_dir() -> "Path":
|
||||
return Path.home() / "Downloads"
|
||||
|
||||
|
||||
def _open_in_file_manager(path: "Path") -> bool:
|
||||
"""Open *path* in the host OS's native file manager.
|
||||
def _open_in_file_manager(folder: "Path", *, select: "Path | None" = None) -> bool:
|
||||
"""Open the OS file manager at *folder*, optionally highlighting *select*.
|
||||
|
||||
Returns ``True`` if the command was dispatched (no guarantee the
|
||||
file manager actually opened — the user might have removed the
|
||||
default handler). Returns ``False`` on platforms we don't know
|
||||
how to launch.
|
||||
Windows
|
||||
``explorer /select,<file>`` is preferred when *select* is given —
|
||||
Explorer pops to the foreground with the freshly-saved file
|
||||
already highlighted. Falls back to ``explorer <folder>``, then
|
||||
``os.startfile`` if both fail; ``os.startfile`` is known to
|
||||
silently no-op or open behind the active window in some Windows
|
||||
configurations, which is why we don't lead with it.
|
||||
macOS
|
||||
``open -R <file>`` reveals the file in Finder.
|
||||
Linux / *BSD
|
||||
``xdg-open`` on the folder. No reliable cross-distro way to
|
||||
highlight a specific file.
|
||||
|
||||
Returns ``True`` if any of the dispatch attempts succeeded
|
||||
(no guarantee the window actually surfaced — the caller should
|
||||
surface a fallback path so the user can paste it manually).
|
||||
"""
|
||||
import os
|
||||
import subprocess
|
||||
try:
|
||||
|
||||
if sys.platform == "win32":
|
||||
os.startfile(str(path)) # type: ignore[attr-defined]
|
||||
# explorer.exe with /select wants a literal "/select," followed
|
||||
# by the path — no space between the comma and the filename, or
|
||||
# Explorer misinterprets and opens "My Computer" instead.
|
||||
attempts = []
|
||||
if select is not None:
|
||||
attempts.append(["explorer", f"/select,{select}"])
|
||||
attempts.append(["explorer", str(folder)])
|
||||
for cmd in attempts:
|
||||
try:
|
||||
subprocess.Popen(cmd)
|
||||
return True
|
||||
except Exception:
|
||||
continue
|
||||
try:
|
||||
os.startfile(str(folder)) # type: ignore[attr-defined]
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
if sys.platform == "darwin":
|
||||
subprocess.Popen(["open", str(path)])
|
||||
try:
|
||||
if select is not None:
|
||||
subprocess.Popen(["open", "-R", str(select)])
|
||||
else:
|
||||
subprocess.Popen(["open", str(folder)])
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
# Linux / *BSD / etc.
|
||||
subprocess.Popen(["xdg-open", str(path)])
|
||||
try:
|
||||
subprocess.Popen(["xdg-open", str(folder)])
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
@@ -353,13 +388,24 @@ def local_download_button(
|
||||
|
||||
saved_path_str = st.session_state.get(saved_key)
|
||||
if saved_path_str:
|
||||
saved_path = Path(saved_path_str)
|
||||
st.success(f"✓ Saved to `{saved_path_str}`")
|
||||
if st.button(
|
||||
"📂 Open Downloads folder",
|
||||
key=open_key,
|
||||
type="secondary",
|
||||
):
|
||||
_open_in_file_manager(Path(saved_path_str).parent)
|
||||
opened = _open_in_file_manager(saved_path.parent, select=saved_path)
|
||||
if opened:
|
||||
# The dispatch returned non-zero; the OS may still have
|
||||
# opened the window behind the active one. Surface a
|
||||
# confirmation so the user knows we tried.
|
||||
st.toast(f"Opening {saved_path.parent}", icon="📂")
|
||||
else:
|
||||
st.warning(
|
||||
f"Could not open the file manager from here. "
|
||||
f"The file is at:\n\n`{saved_path_str}`"
|
||||
)
|
||||
|
||||
|
||||
# Back-compat alias: existing call sites use the old name. New code
|
||||
|
||||
Reference in New Issue
Block a user