class TestRingBuffer(TestCase): def setUp(self): self.b = RingBuffer([], maxlen=5) def test_append_empty(self): self.b.append('x') eq_(self.b, ['x']) def test_append_full(self): self.b.extend("abcde") self.b.append('f') eq_(self.b, ['b', 'c', 'd', 'e', 'f']) def test_extend_empty(self): self.b.extend("abc") eq_(self.b, ['a', 'b', 'c']) def test_extend_overrun(self): self.b.extend("abc") self.b.extend("defg") eq_(self.b, ['c', 'd', 'e', 'f', 'g']) def test_extend_full(self): self.b.extend("abcde") self.b.extend("fgh") eq_(self.b, ['d', 'e', 'f', 'g', 'h'])
def test_zero_maxlen_extend(self): b = RingBuffer([], maxlen=0) b.extend('abcdefghijklmnop') eq_(b, [])
def test_None_maxlen_extend(self): b = RingBuffer([], maxlen=None) b.extend('abcdefghijklmnop') eq_(''.join(b), 'abcdefghijklmnop')
class TestRingBuffer(TestCase): def setUp(self): self.b = RingBuffer([], maxlen=5) def test_append_empty(self): self.b.append('x') eq_(self.b, ['x']) def test_append_full(self): self.b.extend("abcde") self.b.append('f') eq_(self.b, ['b', 'c', 'd', 'e', 'f']) def test_extend_empty(self): self.b.extend("abc") eq_(self.b, ['a', 'b', 'c']) def test_extend_overrun(self): self.b.extend("abc") self.b.extend("defg") eq_(self.b, ['c', 'd', 'e', 'f', 'g']) def test_extend_full(self): self.b.extend("abcde") self.b.extend("fgh") eq_(self.b, ['d', 'e', 'f', 'g', 'h']) def test_plus_equals(self): self.b += "abcdefgh" eq_(self.b, ['d', 'e', 'f', 'g', 'h']) def test_oversized_extend(self): self.b.extend("abcdefghijklmn") eq_(self.b, ['j', 'k', 'l', 'm', 'n']) def test_zero_maxlen_append(self): b = RingBuffer([], maxlen=0) b.append('a') eq_(b, []) def test_zero_maxlen_extend(self): b = RingBuffer([], maxlen=0) b.extend('abcdefghijklmnop') eq_(b, []) def test_None_maxlen_append(self): b = RingBuffer([], maxlen=None) b.append('a') eq_(b, ['a']) def test_None_maxlen_extend(self): b = RingBuffer([], maxlen=None) b.extend('abcdefghijklmnop') eq_(''.join(b), 'abcdefghijklmnop')
class OutputLooper(object): def __init__(self, chan, attr, stream, capture, timeout): self.chan = chan self.stream = stream self.capture = capture self.timeout = timeout self.read_func = getattr(chan, attr) self.prefix = "[%s] %s: " % ( env.host_string, "out" if attr == 'recv' else "err" ) self.printing = getattr(output, 'stdout' if (attr == 'recv') else 'stderr') self.linewise = (env.linewise or env.parallel) self.reprompt = False self.read_size = 4096 self.write_buffer = RingBuffer([], maxlen=len(self.prefix)) def _flush(self, text): if isinstance(text, bytes): text = text.decode('ascii') self.stream.write(text) # Actually only flush if not in linewise mode. # When linewise is set (e.g. in parallel mode) flushing makes # doubling-up of line prefixes, and other mixed output, more likely. if not env.linewise: self.stream.flush() self.write_buffer.extend(text) def loop(self): """ Loop, reading from <chan>.<attr>(), writing to <stream> and buffering to <capture>. Will raise `~fabric.exceptions.CommandTimeout` if network timeouts continue to be seen past the defined ``self.timeout`` threshold. (Timeouts before then are considered part of normal short-timeout fast network reading; see Fabric issue #733 for background.) """ # Initialize loop variables initial_prefix_printed = False seen_cr = False line = [] # Allow prefix to be turned off. if not env.output_prefix: self.prefix = "" start = time.time() while True: # Handle actual read try: bytelist = self.read_func(self.read_size) except socket.timeout: elapsed = time.time() - start if self.timeout is not None and elapsed > self.timeout: raise CommandTimeout(timeout=self.timeout) continue # Empty byte == EOS if len(bytelist) == 0: # If linewise, ensure we flush any leftovers in the buffer. if self.linewise and line: self._flush(self.prefix) self._flush("".join(line)) break # A None capture variable implies that we're in open_shell() if self.capture is None: # Just print directly -- no prefixes, no capturing, nada # And since we know we're using a pty in this mode, just go # straight to stdout. self._flush(bytelist) # Otherwise, we're in run/sudo and need to handle capturing and # prompts. else: # Print to user if self.printing: printable_bytes = bytelist # Small state machine to eat \n after \r if printable_bytes[-1] == "\r": seen_cr = True if printable_bytes[0] == "\n" and seen_cr: printable_bytes = printable_bytes[1:] seen_cr = False while _has_newline(printable_bytes) and printable_bytes != "": # at most 1 split ! cr = re.search("(\r\n|\r|\n)", printable_bytes) if cr is None: break end_of_line = printable_bytes[:cr.start(0)] printable_bytes = printable_bytes[cr.end(0):] if not initial_prefix_printed: self._flush(self.prefix) if _has_newline(end_of_line): end_of_line = '' if self.linewise: self._flush("".join(line) + end_of_line + "\n") line = [] else: self._flush(end_of_line + "\n") initial_prefix_printed = False if self.linewise: line += [printable_bytes] else: if not initial_prefix_printed: self._flush(self.prefix) initial_prefix_printed = True self._flush(printable_bytes) # Now we have handled printing, handle interactivity read_lines = re.split(r"(\r|\n|\r\n)", bytelist.decode()) for fragment in read_lines: # Store in capture buffer self.capture += fragment # Handle prompts expected, response = self._get_prompt_response() if expected: del self.capture[-1 * len(expected):] self.chan.sendall(str(response) + '\n') else: prompt = _endswith(self.capture, env.sudo_prompt) try_again = (_endswith(self.capture, env.again_prompt + '\n') or _endswith(self.capture, env.again_prompt + '\r\n')) if prompt: self.prompt() elif try_again: self.try_again() # Print trailing new line if the last thing we printed was our line # prefix. if self.prefix and "".join(self.write_buffer) == self.prefix: self._flush('\n') def prompt(self): # Obtain cached password, if any password = get_password(*normalize(env.host_string)) # Remove the prompt itself from the capture buffer. This is # backwards compatible with Fabric 0.9.x behavior; the user # will still see the prompt on their screen (no way to avoid # this) but at least it won't clutter up the captured text. del self.capture[-1 * len(env.sudo_prompt):] # If the password we just tried was bad, prompt the user again. if (not password) or self.reprompt: # Print the prompt and/or the "try again" notice if # output is being hidden. In other words, since we need # the user's input, they need to see why we're # prompting them. if not self.printing: self._flush(self.prefix) if self.reprompt: self._flush(env.again_prompt + '\n' + self.prefix) self._flush(env.sudo_prompt) # Prompt for, and store, password. Give empty prompt so the # initial display "hides" just after the actually-displayed # prompt from the remote end. self.chan.input_enabled = False password = fabric.network.prompt_for_password( prompt=" ", no_colon=True, stream=self.stream ) self.chan.input_enabled = True # Update env.password, env.passwords if necessary user, host, port = normalize(env.host_string) # TODO: in 2.x, make sure to only update sudo-specific password # config values, not login ones. set_password(user, host, port, password) # Reset reprompt flag self.reprompt = False # Send current password down the pipe self.chan.sendall(password + '\n') def try_again(self): # Remove text from capture buffer self.capture = self.capture[:len(env.again_prompt)] # Set state so we re-prompt the user at the next prompt. self.reprompt = True def _get_prompt_response(self): """ Iterate through the request prompts dict and return the response and original request if we find a match """ for tup in list(env.prompts.items()): if _endswith(self.capture, tup[0]): return tup return None, None
class OutputLooper(object): def __init__(self, chan, attr, stream, capture, timeout): self.chan = chan self.stream = stream self.capture = capture self.timeout = timeout self.read_func = getattr(chan, attr) self.prefix = "[%s] %s: " % (env.host_string, "out" if attr == 'recv' else "err") self.printing = getattr(output, 'stdout' if (attr == 'recv') else 'stderr') self.linewise = (env.linewise or env.parallel) self.reprompt = False self.read_size = 4096 self.write_buffer = RingBuffer([], maxlen=len(self.prefix)) def _flush(self, text): self.stream.write(text) # Actually only flush if not in linewise mode. # When linewise is set (e.g. in parallel mode) flushing makes # doubling-up of line prefixes, and other mixed output, more likely. if not env.linewise: self.stream.flush() self.write_buffer.extend(text) def loop(self): """ Loop, reading from <chan>.<attr>(), writing to <stream> and buffering to <capture>. Will raise `~fabric.exceptions.CommandTimeout` if network timeouts continue to be seen past the defined ``self.timeout`` threshold. (Timeouts before then are considered part of normal short-timeout fast network reading; see Fabric issue #733 for background.) """ # Internal capture-buffer-like buffer, used solely for state keeping. # Unlike 'capture', nothing is ever purged from this. _buffer = [] # Initialize loop variables initial_prefix_printed = False seen_cr = False line = [] # Allow prefix to be turned off. if not env.output_prefix: self.prefix = "" start = time.time() while True: # Handle actual read try: bytelist = self.read_func(self.read_size) except socket.timeout: elapsed = time.time() - start if self.timeout is not None and elapsed > self.timeout: raise CommandTimeout continue # Empty byte == EOS if bytelist == '': # If linewise, ensure we flush any leftovers in the buffer. if self.linewise and line: self._flush(self.prefix) self._flush("".join(line)) break # A None capture variable implies that we're in open_shell() if self.capture is None: # Just print directly -- no prefixes, no capturing, nada # And since we know we're using a pty in this mode, just go # straight to stdout. self._flush(bytelist) # Otherwise, we're in run/sudo and need to handle capturing and # prompts. else: # Print to user if self.printing: printable_bytes = bytelist # Small state machine to eat \n after \r if printable_bytes[-1] == "\r": seen_cr = True if printable_bytes[0] == "\n" and seen_cr: printable_bytes = printable_bytes[1:] seen_cr = False while _has_newline( printable_bytes) and printable_bytes != "": # at most 1 split ! cr = re.search("(\r\n|\r|\n)", printable_bytes) if cr is None: break end_of_line = printable_bytes[:cr.start(0)] printable_bytes = printable_bytes[cr.end(0):] if not initial_prefix_printed: self._flush(self.prefix) if _has_newline(end_of_line): end_of_line = '' if self.linewise: self._flush("".join(line) + end_of_line + "\n") line = [] else: self._flush(end_of_line + "\n") initial_prefix_printed = False if self.linewise: line += [printable_bytes] else: if not initial_prefix_printed: self._flush(self.prefix) initial_prefix_printed = True self._flush(printable_bytes) # Now we have handled printing, handle interactivity read_lines = re.split(r"(\r|\n|\r\n)", bytelist) for fragment in read_lines: # Store in capture buffer self.capture += fragment # Store in internal buffer _buffer += fragment # Handle prompts expected, response = self._get_prompt_response() if expected: del self.capture[-1 * len(expected):] self.chan.sendall(six.text_type(response) + '\n') else: prompt = _endswith(self.capture, env.sudo_prompt) try_again = (_endswith(self.capture, env.again_prompt + '\n') or _endswith(self.capture, env.again_prompt + '\r\n')) if prompt: self.prompt() elif try_again: self.try_again() # Print trailing new line if the last thing we printed was our line # prefix. if self.prefix and "".join(self.write_buffer) == self.prefix: self._flush('\n') def prompt(self): # Obtain cached password, if any password = get_password(*normalize(env.host_string)) # Remove the prompt itself from the capture buffer. This is # backwards compatible with Fabric 0.9.x behavior; the user # will still see the prompt on their screen (no way to avoid # this) but at least it won't clutter up the captured text. del self.capture[-1 * len(env.sudo_prompt):] # If the password we just tried was bad, prompt the user again. if (not password) or self.reprompt: # Print the prompt and/or the "try again" notice if # output is being hidden. In other words, since we need # the user's input, they need to see why we're # prompting them. if not self.printing: self._flush(self.prefix) if self.reprompt: self._flush(env.again_prompt + '\n' + self.prefix) self._flush(env.sudo_prompt) # Prompt for, and store, password. Give empty prompt so the # initial display "hides" just after the actually-displayed # prompt from the remote end. self.chan.input_enabled = False password = fabric.network.prompt_for_password(prompt=" ", no_colon=True, stream=self.stream) self.chan.input_enabled = True # Update env.password, env.passwords if necessary user, host, port = normalize(env.host_string) set_password(user, host, port, password) # Reset reprompt flag self.reprompt = False # Send current password down the pipe self.chan.sendall(password + '\n') def try_again(self): # Remove text from capture buffer self.capture = self.capture[:len(env.again_prompt)] # Set state so we re-prompt the user at the next prompt. self.reprompt = True def _get_prompt_response(self): """ Iterate through the request prompts dict and return the response and original request if we find a match """ for tup in six.iteritems(env.prompts): if _endswith(self.capture, tup[0]): return tup return None, None
class OutputLooper(object): def __init__(self, chan, attr, stream, capture): self.chan = chan self.stream = stream self.capture = capture self.read_func = getattr(chan, attr) self.prefix = "[%s] %s: " % ( env.host_string, "out" if attr == 'recv' else "err" ) self.printing = getattr(output, 'stdout' if (attr == 'recv') else 'stderr') self.linewise = (env.linewise or env.parallel) self.reprompt = False self.read_size = 4096 self.write_buffer = RingBuffer([], maxlen=len(self.prefix)) def _flush(self, text): self.stream.write(text) self.stream.flush() self.write_buffer.extend(text) def loop(self): """ Loop, reading from <chan>.<attr>(), writing to <stream> and buffering to <capture>. """ # Internal capture-buffer-like buffer, used solely for state keeping. # Unlike 'capture', nothing is ever purged from this. _buffer = [] # Initialize loop variables initial_prefix_printed = False seen_cr = False line = [] # Allow prefix to be turned off. if not env.output_prefix: self.prefix = "" while True: # Handle actual read try: bytelist = self.read_func(self.read_size) except socket.timeout: continue # Empty byte == EOS if bytelist == '': # If linewise, ensure we flush any leftovers in the buffer. if self.linewise and line: self._flush(self.prefix) self._flush("".join(line)) break # A None capture variable implies that we're in open_shell() if self.capture is None: # Just print directly -- no prefixes, no capturing, nada # And since we know we're using a pty in this mode, just go # straight to stdout. self._flush(bytelist) # Otherwise, we're in run/sudo and need to handle capturing and # prompts. else: # Print to user if self.printing: printable_bytes = bytelist # Small state machine to eat \n after \r if printable_bytes[-1] == "\r": seen_cr = True if printable_bytes[0] == "\n" and seen_cr: printable_bytes = printable_bytes[1:] seen_cr = False while _has_newline(printable_bytes) and printable_bytes != "": # at most 1 split ! cr = re.search("(\r\n|\r|\n)", printable_bytes) if cr is None: break end_of_line = printable_bytes[:cr.start(0)] printable_bytes = printable_bytes[cr.end(0):] if not initial_prefix_printed: self._flush(self.prefix) if _has_newline(end_of_line): end_of_line = '' if self.linewise: self._flush("".join(line) + end_of_line + "\n") line = [] else: self._flush(end_of_line + "\n") initial_prefix_printed = False if self.linewise: line += [printable_bytes] else: if not initial_prefix_printed: self._flush(self.prefix) initial_prefix_printed = True self._flush(printable_bytes) # Now we have handled printing, handle interactivity read_lines = re.split(r"(\r|\n|\r\n)", bytelist) for fragment in read_lines: # Store in capture buffer self.capture += fragment # Store in internal buffer _buffer += fragment # Handle prompts prompt = _endswith(self.capture, env.sudo_prompt) try_again = (_endswith(self.capture, env.again_prompt + '\n') or _endswith(self.capture, env.again_prompt + '\r\n')) if prompt: self.prompt() elif try_again: self.try_again() # Print trailing new line if the last thing we printed was our line # prefix. if self.prefix and "".join(self.write_buffer) == self.prefix: self._flush('\n') def prompt(self): # Obtain cached password, if any password = get_password() # Remove the prompt itself from the capture buffer. This is # backwards compatible with Fabric 0.9.x behavior; the user # will still see the prompt on their screen (no way to avoid # this) but at least it won't clutter up the captured text. del self.capture[-1 * len(env.sudo_prompt):] # If the password we just tried was bad, prompt the user again. if (not password) or self.reprompt: # Print the prompt and/or the "try again" notice if # output is being hidden. In other words, since we need # the user's input, they need to see why we're # prompting them. if not self.printing: self._flush(self.prefix) if self.reprompt: self._flush(env.again_prompt + '\n' + self.prefix) self._flush(env.sudo_prompt) # Prompt for, and store, password. Give empty prompt so the # initial display "hides" just after the actually-displayed # prompt from the remote end. self.chan.input_enabled = False password = fabric.network.prompt_for_password( prompt=" ", no_colon=True, stream=self.stream ) self.chan.input_enabled = True # Update env.password, env.passwords if necessary set_password(password) # Reset reprompt flag self.reprompt = False # Send current password down the pipe self.chan.sendall(password + '\n') def try_again(self): # Remove text from capture buffer self.capture = self.capture[:len(env.again_prompt)] # Set state so we re-prompt the user at the next prompt. self.reprompt = True
class OutputLooper(object): def __init__(self, chan, attr, stream, capture, timeout, prefix="", linewise = False): self.chan = chan self.stream = stream self.capture = capture self.timeout = timeout self.read_func = getattr(chan, attr) self.prefix = prefix self.printing = getattr(output, 'stdout' if (attr == 'recv') else 'stderr') self.linewise = linewise self.reprompt = False self.read_size = 4096 self.write_buffer = RingBuffer([], maxlen=len(self.prefix)) def _flush(self, text): self.stream.write(text) self.stream.flush() self.write_buffer.extend(text) def loop(self): """ Loop, reading from <chan>.<attr>(), writing to <stream> and buffering to <capture>. Will raise `~fabric.exceptions.CommandTimeout` if network timeouts continue to be seen past the defined ``self.timeout`` threshold. (Timeouts before then are considered part of normal short-timeout fast network reading; see Fabric issue #733 for background.) """ # Initialize loop variables initial_prefix_printed = False seen_cr = False line = [] dont_capture_next_cr = False start = time.time() while True: # Handle actual read try: bytelist = self.read_func(self.read_size) except socket.timeout: elapsed = time.time() - start if self.timeout is not None and elapsed > self.timeout: raise CommandTimeout continue # Empty byte == EOS if bytelist == '': # If linewise, ensure we flush any leftovers in the buffer. if line: if self.linewise: outputLock.acquire() self._flush(self.prefix) initial_prefix_printed = True self._flush("".join(line)) break # A None capture variable implies that we're in open_shell() if self.capture is None: # Just print directly -- no prefixes, no capturing, nada # And since we know we're using a pty in this mode, just go # straight to stdout. self._flush(bytelist) if self.linewise: outputLock.release() # Otherwise, we're in run/sudo and need to handle capturing and # prompts. else: # Print to user if self.printing: printable_bytes = bytelist # Small state machine to eat \n after \r if printable_bytes[-1] == "\r": seen_cr = True if printable_bytes[0] == "\n" and seen_cr: printable_bytes = printable_bytes[1:] seen_cr = False while _has_newline(printable_bytes) and printable_bytes != "": # at most 1 split ! cr = re.search("(\r\n|\r|\n)", printable_bytes) if cr is None: break end_of_line = printable_bytes[:cr.start(0)] printable_bytes = printable_bytes[cr.end(0):] if not initial_prefix_printed: if self.linewise: outputLock.acquire() self._flush(self.prefix) initial_prefix_printed = True if _has_newline(end_of_line): end_of_line = '' if self.linewise: self._flush("".join(line) + end_of_line + "\n") line = [] else: self._flush(end_of_line + "\n") initial_prefix_printed = False if self.linewise: outputLock.release() if printable_bytes != '': if self.linewise: line += [printable_bytes] else: if not initial_prefix_printed: self._flush(self.prefix) initial_prefix_printed = True self._flush(printable_bytes) # Now we have handled printing, handle interactivity read_lines = re.split(r"(\r|\n|\r\n)", bytelist) for fragment in read_lines: # skip newlines after prompts if dont_capture_next_cr and fragment in ['\r','\n','\r\n']: dont_capture_next_cr = False continue # Store in capture buffer self.capture += fragment # Handle prompts prompt = _endswith(self.capture, env.sudo_prompt) try_again = _endswith(self.capture, env.again_prompt) if prompt: self.prompt() dont_capture_next_cr = True elif try_again: self.try_again() dont_capture_next_cr = True # Print trailing new line in linewise mode if initial_prefix_printed: if self.linewise: self._flush('\n') outputLock.release() def prompt(self): # Obtain cached password, if any password = get_password(*normalize(env.host_string)) # Remove the prompt itself from the capture buffer. This is # backwards compatible with Fabric 0.9.x behavior; the user # will still see the prompt on their screen (no way to avoid # this) but at least it won't clutter up the captured text. del self.capture[-1 * len(env.sudo_prompt):] # If the password we just tried was bad, prompt the user again. if (not password) or self.reprompt: # Print the prompt and/or the "try again" notice if # output is being hidden. In other words, since we need # the user's input, they need to see why we're # prompting them. if not self.printing: self._flush(self.prefix) if self.reprompt: self._flush(env.again_prompt + '\n' + self.prefix) self._flush(env.sudo_prompt) # Prompt for, and store, password. Give empty prompt so the # initial display "hides" just after the actually-displayed # prompt from the remote end. self.chan.input_enabled = False password = fabric.network.prompt_for_password( prompt=" ", no_colon=True, stream=self.stream ) self.chan.input_enabled = True # Update env.password, env.passwords if necessary user, host, port = normalize(env.host_string) set_password(user, host, port, password) # Reset reprompt flag self.reprompt = False # Send current password down the pipe self.chan.sendall(password + '\n') def try_again(self): del self.capture[-1 * len(env.again_prompt):] # Set state so we re-prompt the user at the next prompt. self.reprompt = True