Bug 1422302 - Move mozbuild.controller.building.Footer to mozterm
This makes it a bit easier to share with other parts of the tree,
like test and linting.
MozReview-Commit-ID: 8Gzk8uOF5zK
--- a/python/mozbuild/mozbuild/controller/building.py
+++ b/python/mozbuild/mozbuild/controller/building.py
@@ -26,16 +26,17 @@ from textwrap import (
try:
import psutil
except Exception:
psutil = None
from mach.mixin.logging import LoggingMixin
from mozsystemmonitor.resourcemonitor import SystemResourceMonitor
+from mozterm.widgets import Footer
import mozpack.path as mozpath
from .clobber import (
Clobberer,
)
from ..base import (
BuildEnvironmentNotFoundException,
@@ -558,70 +559,16 @@ class TerminalLoggingHandler(logging.Han
self.footer.draw()
# If we don't flush, the footer may not get drawn.
self.fh.flush()
finally:
self.release()
-class Footer(object):
- """Handles display of a footer in a terminal.
-
- This class implements the functionality common to all mach commands
- that render a footer.
- """
-
- def __init__(self, terminal):
- # terminal is a blessings.Terminal.
- self._t = terminal
- self._fh = sys.stdout
-
- def clear(self):
- """Removes the footer from the current terminal."""
- self._fh.write(self._t.move_x(0))
- self._fh.write(self._t.clear_eol())
-
- def write(self, parts):
- """Write some output in the footer, accounting for terminal width.
-
- parts is a list of 2-tuples of (encoding_function, input).
- None means no encoding."""
-
- # We don't want to write more characters than the current width of the
- # terminal otherwise wrapping may result in weird behavior. We can't
- # simply truncate the line at terminal width characters because a)
- # non-viewable escape characters count towards the limit and b) we
- # don't want to truncate in the middle of an escape sequence because
- # subsequent output would inherit the escape sequence.
- max_width = self._t.width
- written = 0
- write_pieces = []
- for part in parts:
- try:
- func, part = part
- encoded = getattr(self._t, func)(part)
- except ValueError:
- encoded = part
-
- len_part = len(part)
- len_spaces = len(write_pieces)
- if written + len_part + len_spaces > max_width:
- write_pieces.append(part[0:max_width - written - len_spaces])
- written += len_part
- break
-
- write_pieces.append(encoded)
- written += len_part
-
- with self._t.location():
- self._t.move(self._t.height-1,0)
- self._fh.write(' '.join(write_pieces))
-
-
class BuildProgressFooter(Footer):
"""Handles display of a build progress indicator in a terminal.
When mach builds inside a blessings-supported terminal, it will render
progress information collected from a BuildMonitor. This class converts the
state of BuildMonitor into terminal output.
"""
@@ -646,17 +593,16 @@ class BuildProgressFooter(Footer):
elif status == 'finished':
append(('green', tier))
else:
append(('underline_yellow', tier))
self.write(parts)
-
class OutputManager(LoggingMixin):
"""Handles writing job output to a terminal or log."""
def __init__(self, log_manager, footer):
self.populate_logger()
self.footer = None
terminal = log_manager.terminal
new file mode 100644
--- /dev/null
+++ b/python/mozterm/mozterm/widgets.py
@@ -0,0 +1,58 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from __future__ import absolute_import, unicode_literals
+
+from .terminal import Terminal
+
+
+class BaseWidget(object):
+ def __init__(self, terminal=None):
+ self.term = terminal or Terminal()
+ self.stream = self.term.stream
+
+
+class Footer(BaseWidget):
+ """Handles display of a footer in a terminal."""
+
+ def clear(self):
+ """Removes the footer from the current terminal."""
+ self.stream.write(self.term.move_x(0))
+ self.stream.write(self.term.clear_eol())
+
+ def write(self, parts):
+ """Write some output in the footer, accounting for terminal width.
+
+ parts is a list of 2-tuples of (encoding_function, input).
+ None means no encoding."""
+
+ # We don't want to write more characters than the current width of the
+ # terminal otherwise wrapping may result in weird behavior. We can't
+ # simply truncate the line at terminal width characters because a)
+ # non-viewable escape characters count towards the limit and b) we
+ # don't want to truncate in the middle of an escape sequence because
+ # subsequent output would inherit the escape sequence.
+ max_width = self.term.width
+ written = 0
+ write_pieces = []
+ for part in parts:
+ try:
+ func, part = part
+ encoded = getattr(self.term, func)(part)
+ except ValueError:
+ encoded = part
+
+ len_part = len(part)
+ len_spaces = len(write_pieces)
+ if written + len_part + len_spaces > max_width:
+ write_pieces.append(part[0:max_width - written - len_spaces])
+ written += len_part
+ break
+
+ write_pieces.append(encoded)
+ written += len_part
+
+ with self.term.location():
+ self.term.move(self.term.height-1, 0)
+ self.stream.write(' '.join(write_pieces))
--- a/python/mozterm/test/python.ini
+++ b/python/mozterm/test/python.ini
@@ -1,4 +1,5 @@
[DEFAULT]
subsuite = mozterm
[test_terminal.py]
+[test_widgets.py]
new file mode 100644
--- /dev/null
+++ b/python/mozterm/test/test_widgets.py
@@ -0,0 +1,49 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from __future__ import absolute_import, unicode_literals
+
+from io import StringIO
+
+import mozunit
+import pytest
+
+from mozterm import Terminal
+from mozterm.widgets import Footer
+
+
+@pytest.fixture
+def terminal(monkeypatch):
+ blessings = pytest.importorskip('blessings')
+
+ kind = 'xterm-256color'
+ try:
+ term = Terminal(stream=StringIO(), force_styling=True, kind=kind)
+ except blessings.curses.error:
+ pytest.skip("terminal '{}' not found".format(kind))
+
+ # For some reason blessings returns None for width/height though a comment
+ # says that shouldn't ever happen.
+ monkeypatch.setattr(term, '_height_and_width', lambda: (100, 100))
+ return term
+
+
+def test_footer(terminal):
+ footer = Footer(terminal=terminal)
+ footer.write([
+ ('dim', 'foo'),
+ ('green', 'bar'),
+ ])
+ value = terminal.stream.getvalue()
+ expected = "\x1b7\x1b[2mfoo\x1b(B\x1b[m \x1b[32mbar\x1b(B\x1b[m\x1b8"
+ assert value == expected
+
+ footer.clear()
+ value = terminal.stream.getvalue()[len(value):]
+ expected = "\x1b[1G\x1b[K"
+ assert value == expected
+
+
+if __name__ == '__main__':
+ mozunit.main()