-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #76 from Anaconda-Platform/progress-indication
Stream logs to an abstract Frontend interface, and pass through rather than swallowing conda output
- Loading branch information
Showing
54 changed files
with
1,200 additions
and
492 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
# -*- coding: utf-8 -*- | ||
# ---------------------------------------------------------------------------- | ||
# Copyright © 2016, Continuum Analytics, Inc. All rights reserved. | ||
# | ||
# The full license is in the file LICENSE.txt, distributed with this software. | ||
# ---------------------------------------------------------------------------- | ||
"""Frontend class representing a UX.""" | ||
from __future__ import absolute_import | ||
|
||
from abc import ABCMeta, abstractmethod | ||
|
||
from anaconda_project.internal.metaclass import with_metaclass | ||
|
||
|
||
class Frontend(with_metaclass(ABCMeta)): | ||
"""A UX (CLI, GUI, etc.) for project operations.""" | ||
|
||
def __init__(self): | ||
"""Construct a Frontend.""" | ||
self._info_buf = '' | ||
self._error_buf = '' | ||
|
||
def _partial(self, data, buf, line_handler): | ||
buf = buf + data | ||
(start, sep, end) = buf.partition('\n') | ||
while sep != '': | ||
# we do this instead of using os.linesep in case | ||
# something on windows outputs unix-style line | ||
# endings, we don't want to go haywire. On unix when | ||
# we actually want \r to carriage return, we'll be | ||
# overriding this "partial" handler and not using this | ||
# buffering implementation. | ||
if start.endswith('\r'): | ||
start = start[:-1] | ||
line_handler(start) | ||
buf = end | ||
(start, sep, end) = buf.partition('\n') | ||
return buf | ||
|
||
def partial_info(self, data): | ||
"""Log only part of an info-level line. | ||
The default implementation buffers this until a line separator | ||
and then passes the entire line to info(). | ||
Subtypes can override this if they want to print output | ||
immediately as it arrives. | ||
""" | ||
self._info_buf = self._partial(data, self._info_buf, self.info) | ||
|
||
def partial_error(self, data): | ||
"""Log only part of an error-level line. | ||
The default implementation buffers this until a line separator | ||
and then passes the entire line to error(). | ||
Subtypes can override this if they want to print output | ||
immediately as it arrives. | ||
""" | ||
self._error_buf = self._partial(data, self._error_buf, self.error) | ||
|
||
@abstractmethod | ||
def info(self, message): | ||
"""Log an info-level message.""" | ||
pass # pragma: no cover | ||
|
||
@abstractmethod | ||
def error(self, message): | ||
"""Log an error-level message. | ||
A rule of thumb is that if a function also returns a | ||
``Status``, this message should also be appended to the | ||
``errors`` field on that status. | ||
""" | ||
pass # pragma: no cover | ||
|
||
# @abstractmethod | ||
# def new_progress(self): | ||
# """Create an appropriate subtype of Progress.""" | ||
# pass # pragma: no cover | ||
|
||
|
||
class NullFrontend(Frontend): | ||
"""A frontend that doesn't do anything.""" | ||
|
||
def __init__(self): | ||
"""Construct a null frontend.""" | ||
super(NullFrontend, self).__init__() | ||
|
||
def partial_info(self, data): | ||
"""Part of a log message.""" | ||
pass | ||
|
||
def partial_error(self, data): | ||
"""Part of an error message.""" | ||
pass | ||
|
||
def info(self, message): | ||
"""Log an info-level message.""" | ||
pass | ||
|
||
def error(self, message): | ||
"""Log an error-level message.""" | ||
pass | ||
|
||
|
||
_singleton_null_frontend = None | ||
|
||
|
||
def _null_frontend(): | ||
global _singleton_null_frontend | ||
if _singleton_null_frontend is None: | ||
_singleton_null_frontend = NullFrontend() | ||
return _singleton_null_frontend | ||
|
||
|
||
class _ErrorRecordingFrontendProxy(Frontend): | ||
def __init__(self, underlying): | ||
super(_ErrorRecordingFrontendProxy, self).__init__() | ||
self._errors = [] | ||
self.underlying = underlying | ||
|
||
def info(self, message): | ||
"""Log an info-level message.""" | ||
self.underlying.info(message) | ||
|
||
def error(self, message): | ||
"""Log an error-level message.""" | ||
self._errors.append(message) | ||
self.underlying.error(message) | ||
|
||
def pop_errors(self): | ||
result = self._errors | ||
self._errors = [] | ||
return result | ||
|
||
|
||
def _new_error_recorder(frontend): | ||
return _ErrorRecordingFrontendProxy(frontend) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.