Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #745 from fabric/745-doubled-up-line-prefixes

Line prefix issues post-#715
  • Loading branch information...
commit f9817acff4425382fc8fb7e8688ced042c70d16f 2 parents 9f650d7 + 97abbdd
@bitprophet bitprophet authored
Showing with 81 additions and 8 deletions.
  1. +15 −7 fabric/io.py
  2. +36 −0 fabric/utils.py
  3. +30 −1 tests/test_utils.py
View
22 fabric/io.py
@@ -10,6 +10,7 @@
from fabric.auth import get_password, set_password
import fabric.network
from fabric.network import ssh
+from fabric.utils import RingBuffer
if win32:
import msvcrt
@@ -44,6 +45,12 @@ def __init__(self, chan, attr, stream, capture):
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):
"""
@@ -94,7 +101,7 @@ def loop(self):
printable_bytes = printable_bytes[1:]
seen_cr = False
- while _has_newline(printable_bytes) and printable_bytes!="":
+ 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:
@@ -109,10 +116,10 @@ def loop(self):
end_of_line = ''
if self.linewise:
- self._flush("".join(line)+end_of_line+"\n")
+ self._flush("".join(line) + end_of_line + "\n")
line = []
else:
- self._flush(end_of_line+"\n")
+ self._flush(end_of_line + "\n")
initial_prefix_printed = False
if self.linewise:
@@ -139,6 +146,11 @@ def loop(self):
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()
@@ -179,10 +191,6 @@ def try_again(self):
# Set state so we re-prompt the user at the next prompt.
self.reprompt = True
- def _flush(self, text):
- self.stream.write(text)
- self.stream.flush()
-
def input_loop(chan, using_pty):
while not chan.exit_status_ready():
View
36 fabric/utils.py
@@ -312,3 +312,39 @@ def _format_error_output(header, body):
return "\n\n%s %s %s\n\n%s\n\n%s" % (
side, header, side, body, mark * term_width
)
+
+
+# TODO: replace with collections.deque(maxlen=xxx) in Python 2.6
+class RingBuffer(list):
+ def __init__(self, value, maxlen):
+ # Heh.
+ self._super = super(RingBuffer, self)
+ self._maxlen = maxlen
+ return self._super.__init__(value)
+
+ def _free(self):
+ return self._maxlen - len(self)
+
+ def append(self, value):
+ if self._free() == 0:
+ del self[0]
+ return self._super.append(value)
+
+ def extend(self, values):
+ overage = len(values) - self._free()
+ if overage > 0:
+ del self[0:overage]
+ return self._super.extend(values)
+
+ # Paranoia from here on out.
+ def insert(self, index, value):
+ raise ValueError("Can't insert into the middle of a ring buffer!")
+
+ def __setslice__(self, i, j, sequence):
+ raise ValueError("Can't set a slice of a ring buffer!")
+
+ def __setitem__(self, key, value):
+ if isinstance(key, slice):
+ raise ValueError("Can't set a slice of a ring buffer!")
+ else:
+ return self._super.__setitem__(key, value)
View
31 tests/test_utils.py
@@ -1,13 +1,14 @@
from __future__ import with_statement
import sys
+from unittest import TestCase
from fudge import Fake, patched_context, with_fakes
from fudge.patcher import with_patched_object
from nose.tools import eq_
from fabric.state import output, env
-from fabric.utils import warn, indent, abort, puts, fastprint, error
+from fabric.utils import warn, indent, abort, puts, fastprint, error, RingBuffer
from fabric import utils # For patching
from fabric.context_managers import settings, hide
from utils import mock_streams, aborts, FabricTest, assert_contains
@@ -178,3 +179,31 @@ def test_error_includes_stderr_if_given_and_hidden(self):
with hide('stderr'):
error("error message", func=utils.abort, stderr=stderr)
assert_contains(stderr, sys.stderr.getvalue())
+
+
+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'])
Please sign in to comment.
Something went wrong with that request. Please try again.