diff options
-rwxr-xr-x | ranger/ext/rifle.py | 71 |
1 files changed, 66 insertions, 5 deletions
diff --git a/ranger/ext/rifle.py b/ranger/ext/rifle.py index bb84b295..c3544805 100755 --- a/ranger/ext/rifle.py +++ b/ranger/ext/rifle.py @@ -18,9 +18,10 @@ from __future__ import (absolute_import, division, print_function) import os.path import re -from subprocess import Popen, PIPE +from subprocess import PIPE import sys + __version__ = 'rifle 1.9.3' # Options and constants that a user might want to change: @@ -70,6 +71,66 @@ except ImportError: try: + from ranger.ext.popen23 import Popen23 +except ImportError: + # COMPAT: Python 2 (and Python <=3.2) subprocess.Popen objects aren't + # context managers. We don't care about early Python 3 but we do + # want to wrap Python 2's Popen. There's no harm in always using + # this Popen but it is only necessary when used with + # with-statements. This can be removed once we ditch Python 2 + # support. + from contextlib import contextmanager + # pylint: disable=ungrouped-imports + from subprocess import Popen, TimeoutExpired + + try: + from ranger import PY3 + except ImportError: + from sys import version_info + PY3 = version_info[0] >= 3 + + @contextmanager + def Popen23(*args, **kwargs): # pylint: disable=invalid-name + if PY3: + yield Popen(*args, **kwargs) + return + else: + popen2 = Popen(*args, **kwargs) + try: + yield popen2 + finally: + # From Lib/subprocess.py Popen.__exit__: + if popen2.stdout: + popen2.stdout.close() + if popen2.stderr: + popen2.stderr.close() + try: # Flushing a BufferedWriter may raise an error + if popen2.stdin: + popen2.stdin.close() + except KeyboardInterrupt: + # https://bugs.python.org/issue25942 + # In the case of a KeyboardInterrupt we assume the SIGINT + # was also already sent to our child processes. We can't + # block indefinitely as that is not user friendly. + # If we have not already waited a brief amount of time in + # an interrupted .wait() or .communicate() call, do so here + # for consistency. + # pylint: disable=protected-access + if popen2._sigint_wait_secs > 0: + try: + # pylint: disable=no-member + popen2._wait(timeout=popen2._sigint_wait_secs) + except TimeoutExpired: + pass + popen2._sigint_wait_secs = 0 # Note that this's been done. + # pylint: disable=lost-exception + return # resume the KeyboardInterrupt + finally: + # Wait for the process to terminate, to avoid zombies. + popen2.wait() + + +try: from ranger.ext.popen_forked import Popen_forked except ImportError: def Popen_forked(*args, **kwargs): # pylint: disable=invalid-name @@ -259,14 +320,14 @@ class Rifle(object): # pylint: disable=too-many-instance-attributes self._mimetype, _ = mimetypes.guess_type(fname) if not self._mimetype: - with Popen( + with Popen23( ["file", "--mime-type", "-Lb", fname], stdout=PIPE, stderr=PIPE ) as process: mimetype, _ = process.communicate() self._mimetype = mimetype.decode(ENCODING).strip() if self._mimetype == 'application/octet-stream': try: - with Popen( + with Popen23( ["mimetype", "--output-format", "%m", fname], stdout=PIPE, stderr=PIPE, @@ -445,7 +506,7 @@ class Rifle(object): # pylint: disable=too-many-instance-attributes if 'f' in flags or 't' in flags: Popen_forked(cmd, env=self.hook_environment(os.environ)) else: - with Popen( + with Popen23( cmd, env=self.hook_environment(os.environ) ) as process: process.wait() @@ -528,7 +589,7 @@ def main(): # pylint: disable=too-many-locals label = options.p if options.w is not None and not options.l: - with Popen([options.w] + list(positional)) as process: + with Popen23([options.w] + list(positional)) as process: process.wait() else: # Start up rifle |