[logging] Tuning windows vs unix display output.
This commit is contained in:
@ -58,7 +58,10 @@ def run(graph, strategy=None, plugins=None, services=None):
|
|||||||
from bonobo.ext.jupyter import JupyterOutputPlugin
|
from bonobo.ext.jupyter import JupyterOutputPlugin
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
'Failed to load jupyter widget. Easiest way is to install the optional "jupyter" ' 'dependencies with «pip install bonobo[jupyter]», but you can also install a specific ' 'version by yourself.')
|
'Failed to load jupyter widget. Easiest way is to install the optional "jupyter" '
|
||||||
|
'dependencies with «pip install bonobo[jupyter]», but you can also install a specific '
|
||||||
|
'version by yourself.'
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if JupyterOutputPlugin not in plugins:
|
if JupyterOutputPlugin not in plugins:
|
||||||
plugins.append(JupyterOutputPlugin)
|
plugins.append(JupyterOutputPlugin)
|
||||||
|
|||||||
@ -2,7 +2,9 @@ import io
|
|||||||
import sys
|
import sys
|
||||||
from contextlib import redirect_stdout
|
from contextlib import redirect_stdout
|
||||||
|
|
||||||
from colorama import Style, Fore
|
from colorama import Style, Fore, init
|
||||||
|
|
||||||
|
init(wrap=True)
|
||||||
|
|
||||||
from bonobo import settings
|
from bonobo import settings
|
||||||
from bonobo.plugins import Plugin
|
from bonobo.plugins import Plugin
|
||||||
@ -10,6 +12,13 @@ from bonobo.util.term import CLEAR_EOL, MOVE_CURSOR_UP
|
|||||||
|
|
||||||
|
|
||||||
class IOBuffer():
|
class IOBuffer():
|
||||||
|
"""
|
||||||
|
The role of IOBuffer is to overcome the problem of multiple threads wanting to write to stdout at the same time. It
|
||||||
|
works a bit like a videogame: there are two buffers, one that is used to write, and one which is used to read from.
|
||||||
|
On each cycle, we swap the buffers, and the console plugin handle output of the one which is not anymore "active".
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.current = io.StringIO()
|
self.current = io.StringIO()
|
||||||
self.write = self.current.write
|
self.write = self.current.write
|
||||||
@ -32,6 +41,9 @@ class ConsoleOutputPlugin(Plugin):
|
|||||||
Outputs status information to the connected stdout. Can be a TTY, with or without support for colors/cursor
|
Outputs status information to the connected stdout. Can be a TTY, with or without support for colors/cursor
|
||||||
movements, or a non tty (pipe, file, ...). The features are adapted to terminal capabilities.
|
movements, or a non tty (pipe, file, ...). The features are adapted to terminal capabilities.
|
||||||
|
|
||||||
|
On Windows, we'll play a bit differently because we don't know how to manipulate cursor position. We'll only
|
||||||
|
display stats at the very end, and there won't be this "buffering" logic we need to display both stats and stdout.
|
||||||
|
|
||||||
.. attribute:: prefix
|
.. attribute:: prefix
|
||||||
|
|
||||||
String prefix of output lines.
|
String prefix of output lines.
|
||||||
@ -43,17 +55,18 @@ class ConsoleOutputPlugin(Plugin):
|
|||||||
self.counter = 0
|
self.counter = 0
|
||||||
self._append_cache = ''
|
self._append_cache = ''
|
||||||
self.isatty = sys.stdout.isatty()
|
self.isatty = sys.stdout.isatty()
|
||||||
|
self.iswindows = (sys.platform == 'win32')
|
||||||
|
|
||||||
self._stdout = sys.stdout
|
self._stdout = sys.stdout
|
||||||
self.stdout = IOBuffer()
|
self.stdout = IOBuffer()
|
||||||
self.redirect_stdout = redirect_stdout(self.stdout)
|
self.redirect_stdout = redirect_stdout(self._stdout if self.iswindows else self.stdout)
|
||||||
self.redirect_stdout.__enter__()
|
self.redirect_stdout.__enter__()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if self.isatty:
|
if self.isatty and not self.iswindows:
|
||||||
self._write(self.context.parent, rewind=True)
|
self._write(self.context.parent, rewind=True)
|
||||||
else:
|
else:
|
||||||
pass # not a tty
|
pass # not a tty, or windows, so we'll ignore stats output
|
||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
self._write(self.context.parent, rewind=False)
|
self._write(self.context.parent, rewind=False)
|
||||||
@ -62,9 +75,13 @@ class ConsoleOutputPlugin(Plugin):
|
|||||||
def write(self, context, prefix='', rewind=True, append=None):
|
def write(self, context, prefix='', rewind=True, append=None):
|
||||||
t_cnt = len(context)
|
t_cnt = len(context)
|
||||||
|
|
||||||
buffered = self.stdout.switch()
|
if not self.iswindows:
|
||||||
for line in buffered.split('\n')[:-1]:
|
buffered = self.stdout.switch()
|
||||||
print(line + CLEAR_EOL, file=sys.stderr)
|
for line in buffered.split('\n')[:-1]:
|
||||||
|
print(line + CLEAR_EOL, file=sys.stderr)
|
||||||
|
|
||||||
|
alive_color = Style.BRIGHT
|
||||||
|
dead_color = (Style.BRIGHT + Fore.BLACK) if self.iswindows else Fore.BLACK
|
||||||
|
|
||||||
for i in context.graph.topologically_sorted_indexes:
|
for i in context.graph.topologically_sorted_indexes:
|
||||||
node = context[i]
|
node = context[i]
|
||||||
@ -72,14 +89,14 @@ class ConsoleOutputPlugin(Plugin):
|
|||||||
if node.alive:
|
if node.alive:
|
||||||
_line = ''.join(
|
_line = ''.join(
|
||||||
(
|
(
|
||||||
' ', Style.BRIGHT, '+', Style.RESET_ALL, ' ', node.name, name_suffix, ' ',
|
' ', alive_color, '+', Style.RESET_ALL, ' ', node.name, name_suffix, ' ',
|
||||||
node.get_statistics_as_string(), Style.RESET_ALL, ' ',
|
node.get_statistics_as_string(), Style.RESET_ALL, ' ',
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
_line = ''.join(
|
_line = ''.join(
|
||||||
(
|
(
|
||||||
' ', Fore.BLACK, '-', ' ', node.name, name_suffix, ' ', node.get_statistics_as_string(),
|
' ', dead_color, '-', ' ', node.name, name_suffix, ' ', node.get_statistics_as_string(),
|
||||||
Style.RESET_ALL, ' ',
|
Style.RESET_ALL, ' ',
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -78,8 +78,7 @@ def test_install_requirements_for_dir(runner):
|
|||||||
dirname = get_examples_path('types')
|
dirname = get_examples_path('types')
|
||||||
with patch('bonobo.commands.run._install_requirements') as install_mock:
|
with patch('bonobo.commands.run._install_requirements') as install_mock:
|
||||||
runner('run', '--install', dirname)
|
runner('run', '--install', dirname)
|
||||||
install_mock.assert_called_once_with(
|
install_mock.assert_called_once_with(os.path.join(dirname, 'requirements.txt'))
|
||||||
os.path.join(dirname, 'requirements.txt'))
|
|
||||||
|
|
||||||
|
|
||||||
@all_runners
|
@all_runners
|
||||||
@ -87,8 +86,7 @@ def test_install_requirements_for_file(runner):
|
|||||||
dirname = get_examples_path('types')
|
dirname = get_examples_path('types')
|
||||||
with patch('bonobo.commands.run._install_requirements') as install_mock:
|
with patch('bonobo.commands.run._install_requirements') as install_mock:
|
||||||
runner('run', '--install', os.path.join(dirname, 'strings.py'))
|
runner('run', '--install', os.path.join(dirname, 'strings.py'))
|
||||||
install_mock.assert_called_once_with(
|
install_mock.assert_called_once_with(os.path.join(dirname, 'requirements.txt'))
|
||||||
os.path.join(dirname, 'requirements.txt'))
|
|
||||||
|
|
||||||
|
|
||||||
@all_runners
|
@all_runners
|
||||||
|
|||||||
Reference in New Issue
Block a user