Skip to content

fix: restore environment before launching external programs #707

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions tagstudio/src/qt/helpers/file_opener.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import structlog
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QLabel
from src.qt.helpers.silent_popen import silent_Popen

logger = structlog.get_logger(__name__)

Expand Down Expand Up @@ -38,7 +39,7 @@ def open_file(path: str | Path, file_manager: bool = False):

# For some reason, if the args are passed in a list, this will error when the
# path has spaces, even while surrounded in double quotes.
subprocess.Popen(
silent_Popen(
command_name + command_arg,
shell=True,
close_fds=True,
Expand All @@ -47,7 +48,7 @@ def open_file(path: str | Path, file_manager: bool = False):
)
else:
command = f'"{normpath}"'
subprocess.Popen(
silent_Popen(
command,
shell=True,
close_fds=True,
Expand Down Expand Up @@ -79,7 +80,7 @@ def open_file(path: str | Path, file_manager: bool = False):
command_args = [str(path)]
command = shutil.which(command_name)
if command is not None:
subprocess.Popen([command] + command_args, close_fds=True)
silent_Popen([command] + command_args, close_fds=True)
else:
logger.info("Could not find command on system PATH", command=command_name)
except Exception:
Expand Down
24 changes: 19 additions & 5 deletions tagstudio/src/qt/helpers/silent_popen.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Copyright (C) 2024 Travis Abendshien (CyanVoxel).
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
import os
import subprocess
import sys

"""Implementation of subprocess.Popen that does not spawn console windows or log output."""
"""Implementation of subprocess.Popen that does not spawn console windows or log output
and sanitizes pyinstall environment variables."""


def promptless_Popen( # noqa: N802
def silent_Popen( # noqa: N802
args,
bufsize=-1,
executable=None,
Expand All @@ -21,6 +23,7 @@ def promptless_Popen( # noqa: N802
env=None,
universal_newlines=None,
startupinfo=None,
creationflags=0,
restore_signals=True,
start_new_session=False,
pass_fds=(),
Expand All @@ -36,9 +39,20 @@ def promptless_Popen( # noqa: N802
process_group=None,
):
"""Call subprocess.Popen without creating a console window."""
creation_flags = 0
if sys.platform == "win32":
creation_flags = subprocess.CREATE_NO_WINDOW
creationflags |= subprocess.CREATE_NO_WINDOW
import ctypes

ctypes.windll.kernel32.SetDllDirectoryW(None)
elif (
sys.platform == "linux"
or sys.platform.startswith("freebsd")
or sys.platform.startswith("openbsd")
):
# pass clean environment to the subprocess
env = os.environ
original_env = env.get("LD_LIBRARY_PATH_ORIG")
env["LD_LIBRARY_PATH"] = original_env if original_env else ""

return subprocess.Popen(
args=args,
Expand All @@ -54,7 +68,7 @@ def promptless_Popen( # noqa: N802
env=env,
universal_newlines=universal_newlines,
startupinfo=startupinfo,
creationflags=creation_flags,
creationflags=creationflags,
restore_signals=restore_signals,
start_new_session=start_new_session,
pass_fds=pass_fds,
Expand Down
4 changes: 2 additions & 2 deletions tagstudio/src/qt/helpers/vendored/ffmpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import ffmpeg
import structlog
from src.qt.helpers.silent_popen import promptless_Popen
from src.qt.helpers.silent_popen import silent_Popen

logger = structlog.get_logger(__name__)

Expand Down Expand Up @@ -44,7 +44,7 @@ def _probe(filename, cmd=FFPROBE_CMD, timeout=None, **kwargs):
args += [filename]

# PATCHED
p = promptless_Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p = silent_Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
communicate_kwargs = {}
if timeout is not None:
communicate_kwargs["timeout"] = timeout
Expand Down
8 changes: 4 additions & 4 deletions tagstudio/src/qt/helpers/vendored/pydub/audio_segment.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
get_encoder_name,
ratio_to_db,
)
from src.qt.helpers.silent_popen import promptless_Popen
from src.qt.helpers.silent_popen import silent_Popen
from src.qt.helpers.vendored.pydub.utils import _mediainfo_json

basestring = str
Expand Down Expand Up @@ -606,7 +606,7 @@ def is_format(f):

with open(os.devnull, "rb") as devnull:
# PATCHED
p = promptless_Popen(
p = silent_Popen(
conversion_command, stdin=devnull, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
p_out, p_err = p.communicate()
Expand Down Expand Up @@ -785,7 +785,7 @@ def is_format(f):
log_conversion(conversion_command)

# PATCHED
p = promptless_Popen(
p = silent_Popen(
conversion_command,
stdin=stdin_parameter,
stdout=subprocess.PIPE,
Expand Down Expand Up @@ -1012,7 +1012,7 @@ def export(
# read stdin / write stdout
with open(os.devnull, "rb") as devnull:
# PATCHED
p = promptless_Popen(
p = silent_Popen(
conversion_command, stdin=devnull, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
p_out, p_err = p.communicate()
Expand Down
4 changes: 2 additions & 2 deletions tagstudio/src/qt/helpers/vendored/pydub/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
get_extra_info,
get_prober_name,
)
from src.qt.helpers.silent_popen import promptless_Popen
from src.qt.helpers.silent_popen import silent_Popen


def _mediainfo_json(filepath, read_ahead_limit=-1):
Expand Down Expand Up @@ -38,7 +38,7 @@ def _mediainfo_json(filepath, read_ahead_limit=-1):

command = [prober, "-of", "json"] + command_args
# PATCHED
res = promptless_Popen(
res = silent_Popen(
command, stdin=stdin_parameter, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
output, stderr = res.communicate(input=stdin_data)
Expand Down
Loading