1
1
mirror of https://github.com/ytdl-org/youtube-dl synced 2024-11-27 04:26:50 +01:00

[downloader/external] Fix "Resource Warning" in downloader test

* add compat_subprocess_Popen context manager
* apply context manager in FFmpegFD._call_downloader()
This commit is contained in:
dirkf 2024-03-02 15:17:09 +00:00
parent 31a15a7c8d
commit d8f134a664
2 changed files with 51 additions and 18 deletions

View File

@ -2438,9 +2438,9 @@ compat_html_parser_HTMLParser = compat_HTMLParser
compat_html_parser_HTMLParseError = compat_HTMLParseError compat_html_parser_HTMLParseError = compat_HTMLParseError
try: try:
from subprocess import DEVNULL _DEVNULL = subprocess.DEVNULL
compat_subprocess_get_DEVNULL = lambda: DEVNULL compat_subprocess_get_DEVNULL = lambda: _DEVNULL
except ImportError: except AttributeError:
compat_subprocess_get_DEVNULL = lambda: open(os.path.devnull, 'w') compat_subprocess_get_DEVNULL = lambda: open(os.path.devnull, 'w')
try: try:
@ -2958,6 +2958,33 @@ except ImportError:
return exc_val is not None and isinstance(exc_val, self._exceptions or tuple()) return exc_val is not None and isinstance(exc_val, self._exceptions or tuple())
# subprocess.Popen context manager
# avoids leaking handles if .communicate() is not called
try:
_Popen = subprocess.Popen
# check for required context manager attributes
_Popen.__enter__ and _Popen.__exit__
compat_subprocess_Popen = _Popen
except AttributeError:
# not a context manager - make one
from contextlib import contextmanager
@contextmanager
def compat_subprocess_Popen(*args, **kwargs):
popen = None
try:
popen = _Popen(*args, **kwargs)
yield popen
finally:
if popen:
for f in (popen.stdin, popen.stdout, popen.stderr):
if f:
# repeated .close() is OK, but just in case
with compat_contextlib_suppress(EnvironmentError):
f.close()
popen.wait()
# Fix https://github.com/ytdl-org/youtube-dl/issues/4223 # Fix https://github.com/ytdl-org/youtube-dl/issues/4223
# See http://bugs.python.org/issue9161 for what is broken # See http://bugs.python.org/issue9161 for what is broken
def workaround_optparse_bug9161(): def workaround_optparse_bug9161():
@ -3314,6 +3341,7 @@ __all__ = [
'compat_struct_pack', 'compat_struct_pack',
'compat_struct_unpack', 'compat_struct_unpack',
'compat_subprocess_get_DEVNULL', 'compat_subprocess_get_DEVNULL',
'compat_subprocess_Popen',
'compat_tokenize_tokenize', 'compat_tokenize_tokenize',
'compat_urllib_error', 'compat_urllib_error',
'compat_urllib_parse', 'compat_urllib_parse',

View File

@ -11,6 +11,7 @@ from .common import FileDownloader
from ..compat import ( from ..compat import (
compat_setenv, compat_setenv,
compat_str, compat_str,
compat_subprocess_Popen,
) )
from ..postprocessor.ffmpeg import FFmpegPostProcessor, EXT_TO_OUT_FORMATS from ..postprocessor.ffmpeg import FFmpegPostProcessor, EXT_TO_OUT_FORMATS
from ..utils import ( from ..utils import (
@ -483,21 +484,25 @@ class FFmpegFD(ExternalFD):
self._debug_cmd(args) self._debug_cmd(args)
proc = subprocess.Popen(args, stdin=subprocess.PIPE, env=env) # From [1], a PIPE opened in Popen() should be closed, unless
try: # .communicate() is called. Avoid leaking any PIPEs by using Popen
retval = proc.wait() # as a context manager (newer Python 3.x and compat)
except BaseException as e: # Fixes "Resource Warning" in test/test_downloader_external.py
# subprocess.run would send the SIGKILL signal to ffmpeg and the # [1] https://devpress.csdn.net/python/62fde12d7e66823466192e48.html
# mp4 file couldn't be played, but if we ask ffmpeg to quit it with compat_subprocess_Popen(args, stdin=subprocess.PIPE, env=env) as proc:
# produces a file that is playable (this is mostly useful for live try:
# streams). Note that Windows is not affected and produces playable retval = proc.wait()
# files (see https://github.com/ytdl-org/youtube-dl/issues/8300). except BaseException as e:
if isinstance(e, KeyboardInterrupt) and sys.platform != 'win32': # subprocess.run would send the SIGKILL signal to ffmpeg and the
process_communicate_or_kill(proc, b'q') # mp4 file couldn't be played, but if we ask ffmpeg to quit it
else: # produces a file that is playable (this is mostly useful for live
proc.kill() # streams). Note that Windows is not affected and produces playable
proc.wait() # files (see https://github.com/ytdl-org/youtube-dl/issues/8300).
raise if isinstance(e, KeyboardInterrupt) and sys.platform != 'win32':
process_communicate_or_kill(proc, b'q')
else:
proc.kill()
raise
return retval return retval