diff options
author | toonn <toonn@toonn.io> | 2021-08-08 19:40:15 +0200 |
---|---|---|
committer | toonn <toonn@toonn.io> | 2021-08-08 19:40:15 +0200 |
commit | 8bbd1a2ed33d7ba77ee13fc69f7705f6b8b6d440 (patch) | |
tree | bc3b29367a43e20cedb60d08593b18e8623c9887 /ranger | |
parent | fc4430de36d033563d21413ae009409fd7cb3a59 (diff) | |
download | ranger-8bbd1a2ed33d7ba77ee13fc69f7705f6b8b6d440.tar.gz |
popen23: Popen context manager wrapper
Diffstat (limited to 'ranger')
-rw-r--r-- | ranger/ext/popen23.py | 60 |
1 files changed, 60 insertions, 0 deletions
diff --git a/ranger/ext/popen23.py b/ranger/ext/popen23.py new file mode 100644 index 00000000..a0ae86e1 --- /dev/null +++ b/ranger/ext/popen23.py @@ -0,0 +1,60 @@ +# This file is part of ranger, the console file manager. +# License: GNU GPL version 3, see the file "AUTHORS" for details. + +from __future__ import absolute_import + +from contextlib import contextmanager + +from subprocess import Popen, TimeoutExpired + +try: + from ranger import PY3 +except ImportError: + from sys import version_info + PY3 = version_info[0] >= 3 + + +# 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. +@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 has been done. + # pylint: disable=lost-exception + return # resume the KeyboardInterrupt + finally: + # Wait for the process to terminate, to avoid zombies. + popen2.wait() |