Skip to content

Commit aa56ef0

Browse files
Merge pull request #155 from nicoddemus/terminal-chars-per-line-count
add TerminalWriter.chars_on_current_line read-only property
2 parents e5f9547 + d4cf79b commit aa56ef0

File tree

3 files changed

+50
-0
lines changed

3 files changed

+50
-0
lines changed

CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
- turn iniconfig and apipkg into vendored packages and ease de-vendoring for distributions
1010
- fix #68 remove invalid py.test.ensuretemp references
1111
- fix #25 - deprecate path.listdir(sort=callable)
12+
- add ``TerminalWriter.chars_on_current_line`` read-only property that tracks how many characters
13+
have been written to the current line.
1214

1315
1.4.34
1416
====================================================================

py/_io/terminalwriter.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ def __init__(self, file=None, stringio=False, encoding=None):
139139
self._file = file
140140
self.hasmarkup = should_do_markup(file)
141141
self._lastlen = 0
142+
self._chars_on_current_line = 0
142143

143144
@property
144145
def fullwidth(self):
@@ -150,6 +151,19 @@ def fullwidth(self):
150151
def fullwidth(self, value):
151152
self._terminal_width = value
152153

154+
@property
155+
def chars_on_current_line(self):
156+
"""Return the number of characters written so far in the current line.
157+
158+
Please note that this count does not produce correct results after a reline() call,
159+
see #164.
160+
161+
.. versionadded:: 1.5.0
162+
163+
:rtype: int
164+
"""
165+
return self._chars_on_current_line
166+
153167
def _escaped(self, text, esc):
154168
if esc and self.hasmarkup:
155169
text = (''.join(['\x1b[%sm' % cod for cod in esc]) +
@@ -200,12 +214,22 @@ def write(self, msg, **kw):
200214
if msg:
201215
if not isinstance(msg, (bytes, text)):
202216
msg = text(msg)
217+
218+
self._update_chars_on_current_line(msg)
219+
203220
if self.hasmarkup and kw:
204221
markupmsg = self.markup(msg, **kw)
205222
else:
206223
markupmsg = msg
207224
write_out(self._file, markupmsg)
208225

226+
def _update_chars_on_current_line(self, text):
227+
fields = text.rsplit('\n', 1)
228+
if '\n' in text:
229+
self._chars_on_current_line = len(fields[-1])
230+
else:
231+
self._chars_on_current_line += len(fields[-1])
232+
209233
def line(self, s='', **kw):
210234
self.write(s, **kw)
211235
self._checkfill(s)
@@ -229,6 +253,9 @@ def write(self, msg, **kw):
229253
if msg:
230254
if not isinstance(msg, (bytes, text)):
231255
msg = text(msg)
256+
257+
self._update_chars_on_current_line(msg)
258+
232259
oldcolors = None
233260
if self.hasmarkup and kw:
234261
handle = GetStdHandle(STD_OUTPUT_HANDLE)

testing/io_/test_terminalwriter.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,27 @@ class fil:
226226
assert l == set(["2"])
227227

228228

229+
def test_chars_on_current_line():
230+
tw = py.io.TerminalWriter(stringio=True)
231+
232+
written = []
233+
234+
def write_and_check(s, expected):
235+
tw.write(s, bold=True)
236+
written.append(s)
237+
assert tw.chars_on_current_line == expected
238+
assert tw.stringio.getvalue() == ''.join(written)
239+
240+
write_and_check('foo', 3)
241+
write_and_check('bar', 6)
242+
write_and_check('\n', 0)
243+
write_and_check('\n', 0)
244+
write_and_check('\n\n\n', 0)
245+
write_and_check('\nfoo', 3)
246+
write_and_check('\nfbar\nhello', 5)
247+
write_and_check('10', 7)
248+
249+
229250
@pytest.mark.skipif(sys.platform == "win32", reason="win32 has no native ansi")
230251
def test_attr_hasmarkup():
231252
tw = py.io.TerminalWriter(stringio=True)

0 commit comments

Comments
 (0)