Bug 1422302 - Create python/mozterm for sharing terminal blessings across modules r=gps

This is a new module that will provide a place to store some common
abstractions around the 'blessings' module. The main entrypoint is:

    from mozterm import Terminal
    term = Terminal()

If blessings is available, this will return a blessings.Terminal()
object. If it isn't available, or something went wrong on import,
this will return a NullTerminal() object, which is a drop-in
replacement that does no formatting.

MozReview-Commit-ID: 6c63svm4tM5

--HG--
extra : rebase_source : 9ab221774d92a418d9b098d79bb2c88f75d937f8
This commit is contained in:
Andrew Halberstadt 2017-12-04 09:38:24 -05:00
Родитель 914342129b
Коммит 0e697ce235
10 изменённых файлов: 136 добавлений и 33 удалений

Просмотреть файл

@ -2,6 +2,7 @@ mozilla.pth:python/mach
mozilla.pth:python/mozboot mozilla.pth:python/mozboot
mozilla.pth:python/mozbuild mozilla.pth:python/mozbuild
mozilla.pth:python/mozlint mozilla.pth:python/mozlint
mozilla.pth:python/mozterm
mozilla.pth:python/mozversioncontrol mozilla.pth:python/mozversioncontrol
mozilla.pth:third_party/python/blessings mozilla.pth:third_party/python/blessings
mozilla.pth:third_party/python/compare-locales mozilla.pth:third_party/python/compare-locales

Просмотреть файл

@ -40,6 +40,7 @@ PYTHON_UNITTEST_MANIFESTS += [
'mach/mach/test/python.ini', 'mach/mach/test/python.ini',
'mozbuild/dumbmake/test/python.ini', 'mozbuild/dumbmake/test/python.ini',
'mozlint/test/python.ini', 'mozlint/test/python.ini',
'mozterm/test/python.ini',
'mozversioncontrol/test/python.ini', 'mozversioncontrol/test/python.ini',
] ]

Просмотреть файл

@ -4,30 +4,10 @@
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
from mozterm import Terminal
from ..result import ResultContainer from ..result import ResultContainer
try:
import blessings
except ImportError:
blessings = None
class NullTerminal(object):
"""Replacement for `blessings.Terminal()` that does no formatting."""
class NullCallableString(unicode):
"""A dummy callable Unicode stolen from blessings"""
def __new__(cls):
new = unicode.__new__(cls, u'')
return new
def __call__(self, *args):
if len(args) != 1 or isinstance(args[0], int):
return u''
return args[0]
def __getattr__(self, attr):
return self.NullCallableString()
class StylishFormatter(object): class StylishFormatter(object):
"""Formatter based on the eslint default.""" """Formatter based on the eslint default."""
@ -45,11 +25,8 @@ class StylishFormatter(object):
fmt = " {c1}{lineno}{column} {c2}{level}{normal} {message} {c1}{rule}({linter}){normal}" fmt = " {c1}{lineno}{column} {c2}{level}{normal} {message} {c1}{rule}({linter}){normal}"
fmt_summary = "{t.bold}{c}\u2716 {problem} ({error}, {warning}{failure}){t.normal}" fmt_summary = "{t.bold}{c}\u2716 {problem} ({error}, {warning}{failure}){t.normal}"
def __init__(self, disable_colors=None): def __init__(self, disable_colors=False):
if disable_colors or not blessings: self.term = Terminal(disable_styling=disable_colors)
self.term = NullTerminal()
else:
self.term = blessings.Terminal()
self.num_colors = self.term.number_of_colors self.num_colors = self.term.number_of_colors
def color(self, color): def color(self, color):

Просмотреть файл

@ -0,0 +1,6 @@
# 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, NullTerminal # noqa

Просмотреть файл

@ -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
import os
import sys
class NullTerminal(object):
"""Replacement for `blessings.Terminal()` that does no formatting."""
number_of_colors = 0
width = 0
height = 0
def __init__(self, stream=None, **kwargs):
self.stream = stream or sys.__stdout__
try:
self.is_a_tty = os.isatty(self.stream.fileno())
except:
self.is_a_tty = False
class NullCallableString(unicode):
"""A dummy callable Unicode stolen from blessings"""
def __new__(cls):
new = unicode.__new__(cls, '')
return new
def __call__(self, *args):
if len(args) != 1 or isinstance(args[0], int):
return ''
return args[0]
def __getattr__(self, attr):
return self.NullCallableString()
def Terminal(raises=False, disable_styling=False, **kwargs):
if disable_styling:
return NullTerminal(**kwargs)
try:
import blessings
except Exception:
if raises:
raise
return NullTerminal(**kwargs)
return blessings.Terminal(**kwargs)

Просмотреть файл

@ -0,0 +1,4 @@
[DEFAULT]
subsuite = mozterm
[test_terminal.py]

Просмотреть файл

@ -0,0 +1,51 @@
# 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
import os
import sys
import mozunit
import pytest
from mozterm import Terminal, NullTerminal
def test_terminal():
blessings = pytest.importorskip('blessings')
term = Terminal()
assert isinstance(term, blessings.Terminal)
term = Terminal(disable_styling=True)
assert isinstance(term, NullTerminal)
del sys.modules['blessings']
orig = sys.path[:]
for path in orig:
if 'blessings' in path:
sys.path.remove(path)
term = Terminal()
assert isinstance(term, NullTerminal)
with pytest.raises(ImportError):
term = Terminal(raises=True)
sys.path = orig
def test_null_terminal():
term = NullTerminal()
assert term.red("foo") == "foo"
assert term.red == ""
assert term.color(1) == ""
assert term.number_of_colors == 0
assert term.width == 0
assert term.height == 0
assert term.is_a_tty == os.isatty(sys.stdout.fileno())
if __name__ == '__main__':
mozunit.main()

Просмотреть файл

@ -128,6 +128,22 @@ mozlint:
files-changed: files-changed:
- 'python/mozlint/**' - 'python/mozlint/**'
mozterm:
description: python/mozterm unit tests
platform: linux64/opt
treeherder:
symbol: py(term)
worker:
by-platform:
linux64.*:
docker-image: {in-tree: "lint"}
max-run-time: 3600
run:
mach: python-test --subsuite mozterm
when:
files-changed:
- 'python/mozterm/**'
mozversioncontrol: mozversioncontrol:
description: python/mozversioncontrol unit tests description: python/mozversioncontrol unit tests
platform: linux64/opt platform: linux64/opt

Просмотреть файл

@ -12,6 +12,7 @@ flake8:
- python/mach_commands.py - python/mach_commands.py
- python/mozboot - python/mozboot
- python/mozlint - python/mozlint
- python/mozterm
- python/mozversioncontrol - python/mozversioncontrol
- security/manager - security/manager
- taskcluster - taskcluster

Просмотреть файл

@ -11,18 +11,15 @@ import sys
from distutils.spawn import find_executable from distutils.spawn import find_executable
from mozboot.util import get_state_dir from mozboot.util import get_state_dir
from mozterm import Terminal
from .. import preset as pset from .. import preset as pset
from ..cli import BaseTryParser from ..cli import BaseTryParser
from ..tasks import generate_tasks from ..tasks import generate_tasks
from ..vcs import VCSHelper from ..vcs import VCSHelper
try: terminal = Terminal()
import blessings
terminal = blessings.Terminal()
except ImportError:
from mozlint.formatters.stylish import NullTerminal
terminal = NullTerminal()
FZF_NOT_FOUND = """ FZF_NOT_FOUND = """
Could not find the `fzf` binary. Could not find the `fzf` binary.