Don't create console for subprocesses on Windows (#1261)

Closes #1251
This commit is contained in:
pukkandan 2021-10-20 21:49:40 +05:30 committed by GitHub
parent b4b855ebc7
commit d3c93ec2b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 63 additions and 58 deletions

View file

@ -87,10 +87,10 @@
parse_filesize, parse_filesize,
PerRequestProxyHandler, PerRequestProxyHandler,
platform_name, platform_name,
Popen,
PostProcessingError, PostProcessingError,
preferredencoding, preferredencoding,
prepend_extension, prepend_extension,
process_communicate_or_kill,
register_socks_protocols, register_socks_protocols,
RejectedVideoReached, RejectedVideoReached,
render_table, render_table,
@ -578,12 +578,9 @@ def check_deprecated(param, option, suggestion):
stdout=slave, stdout=slave,
stderr=self._err_file) stderr=self._err_file)
try: try:
self._output_process = subprocess.Popen( self._output_process = Popen(['bidiv'] + width_args, **sp_kwargs)
['bidiv'] + width_args, **sp_kwargs
)
except OSError: except OSError:
self._output_process = subprocess.Popen( self._output_process = Popen(['fribidi', '-c', 'UTF-8'] + width_args, **sp_kwargs)
['fribidi', '-c', 'UTF-8'] + width_args, **sp_kwargs)
self._output_channel = os.fdopen(master, 'rb') self._output_channel = os.fdopen(master, 'rb')
except OSError as ose: except OSError as ose:
if ose.errno == errno.ENOENT: if ose.errno == errno.ENOENT:
@ -3280,11 +3277,11 @@ def print_debug_header(self):
if self.params.get('compat_opts'): if self.params.get('compat_opts'):
write_debug('Compatibility options: %s\n' % ', '.join(self.params.get('compat_opts'))) write_debug('Compatibility options: %s\n' % ', '.join(self.params.get('compat_opts')))
try: try:
sp = subprocess.Popen( sp = Popen(
['git', 'rev-parse', '--short', 'HEAD'], ['git', 'rev-parse', '--short', 'HEAD'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
cwd=os.path.dirname(os.path.abspath(__file__))) cwd=os.path.dirname(os.path.abspath(__file__)))
out, err = process_communicate_or_kill(sp) out, err = sp.communicate_or_kill()
out = out.decode().strip() out = out.decode().strip()
if re.match('[0-9a-f]+', out): if re.match('[0-9a-f]+', out):
write_debug('Git HEAD: %s\n' % out) write_debug('Git HEAD: %s\n' % out)

View file

@ -17,7 +17,7 @@
from .utils import ( from .utils import (
bug_reports_message, bug_reports_message,
expand_path, expand_path,
process_communicate_or_kill, Popen,
YoutubeDLCookieJar, YoutubeDLCookieJar,
) )
@ -599,14 +599,14 @@ def _get_mac_keyring_password(browser_keyring_name, logger):
return password.encode('utf-8') return password.encode('utf-8')
else: else:
logger.debug('using find-generic-password to obtain password') logger.debug('using find-generic-password to obtain password')
proc = subprocess.Popen(['security', 'find-generic-password', proc = Popen(
'-w', # write password to stdout ['security', 'find-generic-password',
'-a', browser_keyring_name, # match 'account' '-w', # write password to stdout
'-s', '{} Safe Storage'.format(browser_keyring_name)], # match 'service' '-a', browser_keyring_name, # match 'account'
stdout=subprocess.PIPE, '-s', '{} Safe Storage'.format(browser_keyring_name)], # match 'service'
stderr=subprocess.DEVNULL) stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
try: try:
stdout, stderr = process_communicate_or_kill(proc) stdout, stderr = proc.communicate_or_kill()
if stdout[-1:] == b'\n': if stdout[-1:] == b'\n':
stdout = stdout[:-1] stdout = stdout[:-1]
return stdout return stdout

View file

@ -22,7 +22,7 @@
handle_youtubedl_headers, handle_youtubedl_headers,
check_executable, check_executable,
is_outdated_version, is_outdated_version,
process_communicate_or_kill, Popen,
sanitize_open, sanitize_open,
) )
@ -116,9 +116,8 @@ def _call_downloader(self, tmpfilename, info_dict):
self._debug_cmd(cmd) self._debug_cmd(cmd)
if 'fragments' not in info_dict: if 'fragments' not in info_dict:
p = subprocess.Popen( p = Popen(cmd, stderr=subprocess.PIPE)
cmd, stderr=subprocess.PIPE) _, stderr = p.communicate_or_kill()
_, stderr = process_communicate_or_kill(p)
if p.returncode != 0: if p.returncode != 0:
self.to_stderr(stderr.decode('utf-8', 'replace')) self.to_stderr(stderr.decode('utf-8', 'replace'))
return p.returncode return p.returncode
@ -128,9 +127,8 @@ def _call_downloader(self, tmpfilename, info_dict):
count = 0 count = 0
while count <= fragment_retries: while count <= fragment_retries:
p = subprocess.Popen( p = Popen(cmd, stderr=subprocess.PIPE)
cmd, stderr=subprocess.PIPE) _, stderr = p.communicate_or_kill()
_, stderr = process_communicate_or_kill(p)
if p.returncode == 0: if p.returncode == 0:
break break
# TODO: Decide whether to retry based on error code # TODO: Decide whether to retry based on error code
@ -199,8 +197,8 @@ def _call_downloader(self, tmpfilename, info_dict):
self._debug_cmd(cmd) self._debug_cmd(cmd)
# curl writes the progress to stderr so don't capture it. # curl writes the progress to stderr so don't capture it.
p = subprocess.Popen(cmd) p = Popen(cmd)
process_communicate_or_kill(p) p.communicate_or_kill()
return p.returncode return p.returncode
@ -476,7 +474,7 @@ def _call_downloader(self, tmpfilename, info_dict):
args.append(encodeFilename(ffpp._ffmpeg_filename_argument(tmpfilename), True)) args.append(encodeFilename(ffpp._ffmpeg_filename_argument(tmpfilename), True))
self._debug_cmd(args) self._debug_cmd(args)
proc = subprocess.Popen(args, stdin=subprocess.PIPE, env=env) proc = Popen(args, stdin=subprocess.PIPE, env=env)
if url in ('-', 'pipe:'): if url in ('-', 'pipe:'):
self.on_process_started(proc, proc.stdin) self.on_process_started(proc, proc.stdin)
try: try:
@ -488,7 +486,7 @@ def _call_downloader(self, tmpfilename, info_dict):
# streams). Note that Windows is not affected and produces playable # streams). Note that Windows is not affected and produces playable
# files (see https://github.com/ytdl-org/youtube-dl/issues/8300). # files (see https://github.com/ytdl-org/youtube-dl/issues/8300).
if isinstance(e, KeyboardInterrupt) and sys.platform != 'win32' and url not in ('-', 'pipe:'): if isinstance(e, KeyboardInterrupt) and sys.platform != 'win32' and url not in ('-', 'pipe:'):
process_communicate_or_kill(proc, b'q') proc.communicate_or_kill(b'q')
else: else:
proc.kill() proc.kill()
proc.wait() proc.wait()

View file

@ -12,6 +12,7 @@
encodeFilename, encodeFilename,
encodeArgument, encodeArgument,
get_exe_version, get_exe_version,
Popen,
) )
@ -26,7 +27,7 @@ def run_rtmpdump(args):
start = time.time() start = time.time()
resume_percent = None resume_percent = None
resume_downloaded_data_len = None resume_downloaded_data_len = None
proc = subprocess.Popen(args, stderr=subprocess.PIPE) proc = Popen(args, stderr=subprocess.PIPE)
cursor_in_new_line = True cursor_in_new_line = True
proc_stderr_closed = False proc_stderr_closed = False
try: try:

View file

@ -17,7 +17,7 @@
get_exe_version, get_exe_version,
is_outdated_version, is_outdated_version,
std_headers, std_headers,
process_communicate_or_kill, Popen,
) )
@ -223,11 +223,10 @@ def get(self, url, html=None, video_id=None, note=None, note2='Executing JS on w
else: else:
self.extractor.to_screen('%s: %s' % (video_id, note2)) self.extractor.to_screen('%s: %s' % (video_id, note2))
p = subprocess.Popen([ p = Popen(
self.exe, '--ssl-protocol=any', [self.exe, '--ssl-protocol=any', self._TMP_FILES['script'].name],
self._TMP_FILES['script'].name stdout=subprocess.PIPE, stderr=subprocess.PIPE)
], stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate_or_kill()
out, err = process_communicate_or_kill(p)
if p.returncode != 0: if p.returncode != 0:
raise ExtractorError( raise ExtractorError(
'Executing JS failed\n:' + encodeArgument(err)) 'Executing JS failed\n:' + encodeArgument(err))

View file

@ -26,9 +26,9 @@
encodeArgument, encodeArgument,
encodeFilename, encodeFilename,
error_to_compat_str, error_to_compat_str,
Popen,
PostProcessingError, PostProcessingError,
prepend_extension, prepend_extension,
process_communicate_or_kill,
shell_quote, shell_quote,
) )
@ -183,8 +183,8 @@ def run(self, info):
self._report_run('atomicparsley', filename) self._report_run('atomicparsley', filename)
self.write_debug('AtomicParsley command line: %s' % shell_quote(cmd)) self.write_debug('AtomicParsley command line: %s' % shell_quote(cmd))
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = process_communicate_or_kill(p) stdout, stderr = p.communicate_or_kill()
if p.returncode != 0: if p.returncode != 0:
msg = stderr.decode('utf-8', 'replace').strip() msg = stderr.decode('utf-8', 'replace').strip()
raise EmbedThumbnailPPError(msg) raise EmbedThumbnailPPError(msg)

View file

@ -20,9 +20,9 @@
is_outdated_version, is_outdated_version,
ISO639Utils, ISO639Utils,
orderedSet, orderedSet,
Popen,
PostProcessingError, PostProcessingError,
prepend_extension, prepend_extension,
process_communicate_or_kill,
replace_extension, replace_extension,
shell_quote, shell_quote,
traverse_obj, traverse_obj,
@ -178,10 +178,8 @@ def get_audio_codec(self, path):
encodeArgument('-i')] encodeArgument('-i')]
cmd.append(encodeFilename(self._ffmpeg_filename_argument(path), True)) cmd.append(encodeFilename(self._ffmpeg_filename_argument(path), True))
self.write_debug('%s command line: %s' % (self.basename, shell_quote(cmd))) self.write_debug('%s command line: %s' % (self.basename, shell_quote(cmd)))
handle = subprocess.Popen( handle = Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
cmd, stderr=subprocess.PIPE, stdout_data, stderr_data = handle.communicate_or_kill()
stdout=subprocess.PIPE, stdin=subprocess.PIPE)
stdout_data, stderr_data = process_communicate_or_kill(handle)
expected_ret = 0 if self.probe_available else 1 expected_ret = 0 if self.probe_available else 1
if handle.wait() != expected_ret: if handle.wait() != expected_ret:
return None return None
@ -223,7 +221,7 @@ def get_metadata_object(self, path, opts=[]):
cmd += opts cmd += opts
cmd.append(encodeFilename(self._ffmpeg_filename_argument(path), True)) cmd.append(encodeFilename(self._ffmpeg_filename_argument(path), True))
self.write_debug('ffprobe command line: %s' % shell_quote(cmd)) self.write_debug('ffprobe command line: %s' % shell_quote(cmd))
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) p = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
stdout, stderr = p.communicate() stdout, stderr = p.communicate()
return json.loads(stdout.decode('utf-8', 'replace')) return json.loads(stdout.decode('utf-8', 'replace'))
@ -284,8 +282,8 @@ def make_args(file, args, name, number):
for i, (path, opts) in enumerate(path_opts) if path) for i, (path, opts) in enumerate(path_opts) if path)
self.write_debug('ffmpeg command line: %s' % shell_quote(cmd)) self.write_debug('ffmpeg command line: %s' % shell_quote(cmd))
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) p = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
stdout, stderr = process_communicate_or_kill(p) stdout, stderr = p.communicate_or_kill()
if p.returncode not in variadic(expected_retcodes): if p.returncode not in variadic(expected_retcodes):
stderr = stderr.decode('utf-8', 'replace').strip() stderr = stderr.decode('utf-8', 'replace').strip()
self.write_debug(stderr) self.write_debug(stderr)

View file

@ -11,9 +11,9 @@
encodeFilename, encodeFilename,
shell_quote, shell_quote,
str_or_none, str_or_none,
Popen,
PostProcessingError, PostProcessingError,
prepend_extension, prepend_extension,
process_communicate_or_kill,
) )
@ -81,8 +81,8 @@ def run(self, information):
self.write_debug('sponskrub command line: %s' % shell_quote(cmd)) self.write_debug('sponskrub command line: %s' % shell_quote(cmd))
pipe = None if self.get_param('verbose') else subprocess.PIPE pipe = None if self.get_param('verbose') else subprocess.PIPE
p = subprocess.Popen(cmd, stdout=pipe) p = Popen(cmd, stdout=pipe)
stdout = process_communicate_or_kill(p)[0] stdout = p.communicate_or_kill()[0]
if p.returncode == 0: if p.returncode == 0:
os.replace(temp_filename, filename) os.replace(temp_filename, filename)

View file

@ -10,7 +10,7 @@
from zipimport import zipimporter from zipimport import zipimporter
from .compat import compat_realpath from .compat import compat_realpath
from .utils import encode_compat_str from .utils import encode_compat_str, Popen
from .version import __version__ from .version import __version__
@ -191,7 +191,7 @@ def get_sha256sum(bin_or_exe, version):
return return
try: try:
# Continues to run in the background # Continues to run in the background
subprocess.Popen( Popen(
'ping 127.0.0.1 -n 5 -w 1000 & del /F "%s.old"' % exe, 'ping 127.0.0.1 -n 5 -w 1000 & del /F "%s.old"' % exe,
shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
ydl.to_screen('Updated yt-dlp to version %s' % version_id) ydl.to_screen('Updated yt-dlp to version %s' % version_id)

View file

@ -2272,6 +2272,20 @@ def process_communicate_or_kill(p, *args, **kwargs):
raise raise
class Popen(subprocess.Popen):
if sys.platform == 'win32':
_startupinfo = subprocess.STARTUPINFO()
_startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
else:
_startupinfo = None
def __init__(self, *args, **kwargs):
super(Popen, self).__init__(*args, **kwargs, startupinfo=self._startupinfo)
def communicate_or_kill(self, *args, **kwargs):
return process_communicate_or_kill(self, *args, **kwargs)
def get_subprocess_encoding(): def get_subprocess_encoding():
if sys.platform == 'win32' and sys.getwindowsversion()[0] >= 5: if sys.platform == 'win32' and sys.getwindowsversion()[0] >= 5:
# For subprocess calls, encode with locale encoding # For subprocess calls, encode with locale encoding
@ -3977,8 +3991,7 @@ def check_executable(exe, args=[]):
""" Checks if the given binary is installed somewhere in PATH, and returns its name. """ Checks if the given binary is installed somewhere in PATH, and returns its name.
args can be a list of arguments for a short output (like -version) """ args can be a list of arguments for a short output (like -version) """
try: try:
process_communicate_or_kill(subprocess.Popen( Popen([exe] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate_or_kill()
[exe] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE))
except OSError: except OSError:
return False return False
return exe return exe
@ -3992,10 +4005,9 @@ def get_exe_version(exe, args=['--version'],
# STDIN should be redirected too. On UNIX-like systems, ffmpeg triggers # STDIN should be redirected too. On UNIX-like systems, ffmpeg triggers
# SIGTTOU if yt-dlp is run in the background. # SIGTTOU if yt-dlp is run in the background.
# See https://github.com/ytdl-org/youtube-dl/issues/955#issuecomment-209789656 # See https://github.com/ytdl-org/youtube-dl/issues/955#issuecomment-209789656
out, _ = process_communicate_or_kill(subprocess.Popen( out, _ = Popen(
[encodeArgument(exe)] + args, [encodeArgument(exe)] + args, stdin=subprocess.PIPE,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate_or_kill()
stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
except OSError: except OSError:
return False return False
if isinstance(out, bytes): # Python 2.x if isinstance(out, bytes): # Python 2.x
@ -6155,11 +6167,11 @@ def write_xattr(path, key, value):
+ [encodeFilename(path, True)]) + [encodeFilename(path, True)])
try: try:
p = subprocess.Popen( p = Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
except EnvironmentError as e: except EnvironmentError as e:
raise XAttrMetadataError(e.errno, e.strerror) raise XAttrMetadataError(e.errno, e.strerror)
stdout, stderr = process_communicate_or_kill(p) stdout, stderr = p.communicate_or_kill()
stderr = stderr.decode('utf-8', 'replace') stderr = stderr.decode('utf-8', 'replace')
if p.returncode != 0: if p.returncode != 0:
raise XAttrMetadataError(p.returncode, stderr) raise XAttrMetadataError(p.returncode, stderr)