From d9b70c8a9bbf3db14bec2a0908c1bc14526ca87c Mon Sep 17 00:00:00 2001 From: Brice Fernandes Date: Thu, 7 Jun 2012 16:30:41 +0100 Subject: [PATCH] Backward-compatible Port to Python 3 - Keeps Python 2.7 and 2.6 compatibility - Is not compatible with Python 2.5, as the original code never was. This is due to two problems: 1. Use of the `Thread.is_alive()` method. Python 2.5 only provides `Thread.isAlive()`. 2. Use of the `Popen.terminate()` and `Popen.kill()` methods, which only exist in Python >= 2.6 --- envoy/__init__.py | 4 +-- envoy/core.py | 14 ++++++++--- envoy/packages/pexpect.py | 52 +++++++++++++++++++-------------------- setup.py | 6 ++--- 4 files changed, 41 insertions(+), 35 deletions(-) diff --git a/envoy/__init__.py b/envoy/__init__.py index bc08008..8710c6b 100644 --- a/envoy/__init__.py +++ b/envoy/__init__.py @@ -1,3 +1,3 @@ -from core import * +from .core import * -from core import __version__ \ No newline at end of file +from .core import __version__ diff --git a/envoy/core.py b/envoy/core.py index 665289f..4e66b2e 100644 --- a/envoy/core.py +++ b/envoy/core.py @@ -8,6 +8,7 @@ This module provides envoy awesomeness. """ import os +import sys import shlex import subprocess import threading @@ -43,8 +44,13 @@ class Command(object): stderr=subprocess.PIPE, bufsize=0, ) - - self.out, self.err = self.process.communicate(self.data) + if sys.version_info[0] >= 3: + self.out, self.err = self.process.communicate( + input = bytes(self.data, "UTF-8") if self.data else None + ) + else: + self.out, self.err = self.process.communicate(self.data) + thread = threading.Thread(target=target) thread.start() @@ -139,7 +145,7 @@ def expand_args(command): """Parses command strings and returns a Popen-ready list.""" # Prepare arguments. - if isinstance(command, basestring): + if isinstance(command, str): splitter = shlex.shlex(command) splitter.whitespace = '|' splitter.whitespace_split = True @@ -152,7 +158,7 @@ def expand_args(command): else: break - command = map(shlex.split, command) + command = list(map(shlex.split, command)) return command diff --git a/envoy/packages/pexpect.py b/envoy/packages/pexpect.py index 1965b33..fca714b 100644 --- a/envoy/packages/pexpect.py +++ b/envoy/packages/pexpect.py @@ -80,7 +80,7 @@ try: import errno import traceback import signal -except ImportError, e: +except ImportError as e: raise ImportError (str(e) + """ A critical module was not found. Probably this operating system does not @@ -217,8 +217,8 @@ def run (command, timeout=-1, withexitstatus=False, events=None, extra_args=None else: child = spawn(command, timeout=timeout, maxread=2000, logfile=logfile, cwd=cwd, env=env) if events is not None: - patterns = events.keys() - responses = events.values() + patterns = list(events.keys()) + responses = list(events.values()) else: patterns=None # We assume that EOF or TIMEOUT will save us. responses=None @@ -227,16 +227,16 @@ def run (command, timeout=-1, withexitstatus=False, events=None, extra_args=None while 1: try: index = child.expect (patterns) - if type(child.after) in types.StringTypes: + if type(child.after) in str: child_result_list.append(child.before + child.after) else: # child.after may have been a TIMEOUT or EOF, so don't cat those. child_result_list.append(child.before) - if type(responses[index]) in types.StringTypes: + if type(responses[index]) in str: child.send(responses[index]) elif type(responses[index]) is types.FunctionType: callback_result = responses[index](locals()) sys.stdout.flush() - if type(callback_result) in types.StringTypes: + if type(callback_result) in str: child.send(callback_result) elif callback_result: break @@ -519,7 +519,7 @@ class spawn (object): if self.use_native_pty_fork: try: self.pid, self.child_fd = pty.fork() - except OSError, e: + except OSError as e: raise ExceptionPexpect('Error! pty.fork() failed: ' + str(e)) else: # Use internal __fork_pty self.pid, self.child_fd = self.__fork_pty() @@ -574,11 +574,11 @@ class spawn (object): parent_fd, child_fd = os.openpty() if parent_fd < 0 or child_fd < 0: - raise ExceptionPexpect, "Error! Could not open pty with os.openpty()." + raise ExceptionPexpect("Error! Could not open pty with os.openpty().") pid = os.fork() if pid < 0: - raise ExceptionPexpect, "Error! Failed os.fork()." + raise ExceptionPexpect("Error! Failed os.fork().") elif pid == 0: # Child. os.close(parent_fd) @@ -615,7 +615,7 @@ class spawn (object): fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY); if fd >= 0: os.close(fd) - raise ExceptionPexpect, "Error! We are not disconnected from a controlling tty." + raise ExceptionPexpect("Error! We are not disconnected from a controlling tty.") except: # Good! We are disconnected from a controlling tty. pass @@ -623,14 +623,14 @@ class spawn (object): # Verify we can open child pty. fd = os.open(child_name, os.O_RDWR); if fd < 0: - raise ExceptionPexpect, "Error! Could not open child pty, " + child_name + raise ExceptionPexpect("Error! Could not open child pty, " + child_name) else: os.close(fd) # Verify we now have a controlling tty. fd = os.open("/dev/tty", os.O_WRONLY) if fd < 0: - raise ExceptionPexpect, "Error! Could not open controlling tty, /dev/tty" + raise ExceptionPexpect("Error! Could not open controlling tty, /dev/tty") else: os.close(fd) @@ -878,7 +878,7 @@ class spawn (object): return self - def next (self): # File-like object. + def __next__ (self): # File-like object. """This is to support iterators over a file-like object. """ @@ -1098,7 +1098,7 @@ class spawn (object): try: pid, status = os.waitpid(self.pid, waitpid_options) - except OSError, e: # No child processes + except OSError as e: # No child processes if e[0] == errno.ECHILD: raise ExceptionPexpect ('isalive() encountered condition where "terminated" is 0, but there was no child process. Did someone else call waitpid() on our process?') else: @@ -1110,7 +1110,7 @@ class spawn (object): if pid == 0: try: pid, status = os.waitpid(self.pid, waitpid_options) ### os.WNOHANG) # Solaris! - except OSError, e: # This should never happen... + except OSError as e: # This should never happen... if e[0] == errno.ECHILD: raise ExceptionPexpect ('isalive() encountered condition that should never happen. There was no child process. Did someone else call waitpid() on our process?') else: @@ -1175,7 +1175,7 @@ class spawn (object): if patterns is None: return [] - if type(patterns) is not types.ListType: + if type(patterns) is not list: patterns = [patterns] compile_flags = re.DOTALL # Allow dot to match \n @@ -1183,7 +1183,7 @@ class spawn (object): compile_flags = compile_flags | re.IGNORECASE compiled_pattern_list = [] for p in patterns: - if type(p) in types.StringTypes: + if type(p) in str: compiled_pattern_list.append(re.compile(p, compile_flags)) elif p is EOF: compiled_pattern_list.append(EOF) @@ -1301,7 +1301,7 @@ class spawn (object): This method is also useful when you don't want to have to worry about escaping regular expression characters that you want to match.""" - if type(pattern_list) in types.StringTypes or pattern_list in (TIMEOUT, EOF): + if type(pattern_list) in str or pattern_list in (TIMEOUT, EOF): pattern_list = [pattern_list] return self.expect_loop(searcher_string(pattern_list), timeout, searchwindowsize) @@ -1343,7 +1343,7 @@ class spawn (object): incoming = incoming + c if timeout is not None: timeout = end_time - time.time() - except EOF, e: + except EOF as e: self.buffer = '' self.before = incoming self.after = EOF @@ -1356,7 +1356,7 @@ class spawn (object): self.match = None self.match_index = None raise EOF (str(e) + '\n' + str(self)) - except TIMEOUT, e: + except TIMEOUT as e: self.buffer = incoming self.before = incoming self.after = TIMEOUT @@ -1380,7 +1380,7 @@ class spawn (object): """This returns the terminal window size of the child tty. The return value is a tuple of (rows, cols). """ - TIOCGWINSZ = getattr(termios, 'TIOCGWINSZ', 1074295912L) + TIOCGWINSZ = getattr(termios, 'TIOCGWINSZ', 1074295912) s = struct.pack('HHHH', 0, 0, 0, 0) x = fcntl.ioctl(self.fileno(), TIOCGWINSZ, s) return struct.unpack('HHHH', x)[0:2] @@ -1401,7 +1401,7 @@ class spawn (object): # Newer versions of Linux have totally different values for TIOCSWINSZ. # Note that this fix is a hack. TIOCSWINSZ = getattr(termios, 'TIOCSWINSZ', -2146929561) - if TIOCSWINSZ == 2148037735L: # L is not required in Python >= 2.2. + if TIOCSWINSZ == 2148037735: # L is not required in Python >= 2.2. TIOCSWINSZ = -2146929561 # Same bits, but with sign. # Note, assume ws_xpixel and ws_ypixel are zero. s = struct.pack('HHHH', r, c, 0, 0) @@ -1502,7 +1502,7 @@ class spawn (object): while True: try: return select.select (iwtd, owtd, ewtd, timeout) - except select.error, e: + except select.error as e: if e[0] == errno.EINTR: # if we loop back we have to subtract the amount of time we already waited. if timeout is not None: @@ -1554,7 +1554,7 @@ class searcher_string (object): self.eof_index = -1 self.timeout_index = -1 self._strings = [] - for n, s in zip(range(len(strings)), strings): + for n, s in zip(list(range(len(strings))), strings): if s is EOF: self.eof_index = n continue @@ -1648,7 +1648,7 @@ class searcher_re (object): self.eof_index = -1 self.timeout_index = -1 self._searches = [] - for n, s in zip(range(len(patterns)), patterns): + for n, s in zip(list(range(len(patterns))), patterns): if s is EOF: self.eof_index = n continue @@ -1715,7 +1715,7 @@ def which(filename): if os.access (filename, os.X_OK): return filename - if not os.environ.has_key('PATH') or os.environ['PATH'] == '': + if 'PATH' not in os.environ or os.environ['PATH'] == '': p = os.defpath else: p = os.environ['PATH'] diff --git a/setup.py b/setup.py index 3e8c978..c3444ae 100755 --- a/setup.py +++ b/setup.py @@ -35,10 +35,10 @@ setup( 'Natural Language :: English', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', - 'Programming Language :: Python :: 2.5', + # 'Programming Language :: Python :: 2.5', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', - # 'Programming Language :: Python :: 3.0', - # 'Programming Language :: Python :: 3.1', + 'Programming Language :: Python :: 3.0', + 'Programming Language :: Python :: 3.1', ), )