diff --git a/taskflow/engines/worker_based/worker.py b/taskflow/engines/worker_based/worker.py index f75b7a8d5..5464c8142 100644 --- a/taskflow/engines/worker_based/worker.py +++ b/taskflow/engines/worker_based/worker.py @@ -141,8 +141,13 @@ class Worker(object): pass tpl_params['platform'] = platform.platform() tpl_params['thread_id'] = tu.get_ident() - return BANNER_TEMPLATE.substitute(BANNER_TEMPLATE.defaults, - **tpl_params) + banner = BANNER_TEMPLATE.substitute(BANNER_TEMPLATE.defaults, + **tpl_params) + # NOTE(harlowja): this is needed since the template in this file + # will always have newlines that end with '\n' (even on different + # platforms due to the way this source file is encoded) so we have + # to do this little dance to make it platform neutral... + return misc.fix_newlines(banner) def run(self, display_banner=True, banner_writer=None): """Runs the worker.""" diff --git a/taskflow/exceptions.py b/taskflow/exceptions.py index c66327542..d4db4e5f6 100644 --- a/taskflow/exceptions.py +++ b/taskflow/exceptions.py @@ -14,6 +14,7 @@ # License for the specific language governing permissions and limitations # under the License. +import os import traceback import six @@ -46,14 +47,19 @@ class TaskFlowException(Exception): """Pretty formats a taskflow exception + any connected causes.""" if indent < 0: raise ValueError("indent must be greater than or equal to zero") - return "\n".join(self._pformat(self, [], 0, - indent=indent, indent_text=indent_text)) + return os.linesep.join(self._pformat(self, [], 0, + indent=indent, + indent_text=indent_text)) @classmethod def _pformat(cls, excp, lines, current_indent, indent=2, indent_text=" "): line_prefix = indent_text * current_indent for line in traceback.format_exception_only(type(excp), excp): # We'll add our own newlines on at the end of formatting. + # + # NOTE(harlowja): the reason we don't search for os.linesep is + # that the traceback module seems to only use '\n' (for some + # reason). if line.endswith("\n"): line = line[0:-1] lines.append(line_prefix + line) diff --git a/taskflow/listeners/claims.py b/taskflow/listeners/claims.py index 7fa864747..82b89cf0e 100644 --- a/taskflow/listeners/claims.py +++ b/taskflow/listeners/claims.py @@ -17,6 +17,7 @@ from __future__ import absolute_import import logging +import os import six @@ -70,7 +71,8 @@ class CheckingClaimListener(base.Listener): engine.suspend() except exceptions.TaskFlowException as e: LOG.warn("Failed suspending engine '%s', (previously owned by" - " '%s'):\n%s", engine, self._owner, e.pformat()) + " '%s'):%s%s", engine, self._owner, os.linesep, + e.pformat()) def _flow_receiver(self, state, details): self._claim_checker(state, details) diff --git a/taskflow/listeners/logging.py b/taskflow/listeners/logging.py index c54baf6a4..4995e9f28 100644 --- a/taskflow/listeners/logging.py +++ b/taskflow/listeners/logging.py @@ -17,6 +17,7 @@ from __future__ import absolute_import import logging as logging_base +import os import sys from taskflow.listeners import base @@ -148,7 +149,7 @@ class DynamicLoggingListener(base.Listener): # exc_info that can be used but we *should* have a string # version that we can use instead... exc_info = None - exc_details = "\n%s" % fail.pformat(traceback=True) + exc_details = "%s%s" % (os.linesep, fail.pformat(traceback=True)) return (exc_info, exc_details) def _flow_receiver(self, state, details): diff --git a/taskflow/types/failure.py b/taskflow/types/failure.py index aba04d4ac..ae848d316 100644 --- a/taskflow/types/failure.py +++ b/taskflow/types/failure.py @@ -15,6 +15,7 @@ # under the License. import copy +import os import sys import traceback @@ -280,10 +281,13 @@ class Failure(object): else: traceback_str = None if traceback_str: - buf.write('\nTraceback (most recent call last):\n') + buf.write(os.linesep) + buf.write('Traceback (most recent call last):') + buf.write(os.linesep) buf.write(traceback_str) else: - buf.write('\nTraceback not available.') + buf.write(os.linesep) + buf.write('Traceback not available.') return buf.getvalue() def __iter__(self): diff --git a/taskflow/types/graph.py b/taskflow/types/graph.py index 22b6b71cc..068a8e20a 100644 --- a/taskflow/types/graph.py +++ b/taskflow/types/graph.py @@ -15,6 +15,7 @@ # under the License. import collections +import os import networkx as nx import six @@ -78,7 +79,7 @@ class DiGraph(nx.DiGraph): buf.write(" --> %s" % (cycle[i])) buf.write(" --> %s" % (cycle[0])) lines.append(" %s" % buf.getvalue()) - return "\n".join(lines) + return os.linesep.join(lines) def export_to_dot(self): """Exports the graph to a dot format (requires pydot library).""" diff --git a/taskflow/types/table.py b/taskflow/types/table.py index b62572000..6813fab1a 100644 --- a/taskflow/types/table.py +++ b/taskflow/types/table.py @@ -15,6 +15,7 @@ # under the License. import itertools +import os import six @@ -39,6 +40,7 @@ class PleasantTable(object): COLUMN_SEPARATOR_CHAR = '|' HEADER_FOOTER_JOINING_CHAR = '+' HEADER_FOOTER_CHAR = '-' + LINE_SEP = os.linesep @staticmethod def _center_text(text, max_len, fill=' '): @@ -87,7 +89,7 @@ class PleasantTable(object): # Build the main header. content_buf = six.StringIO() content_buf.write(header_footer_buf.getvalue()) - content_buf.write("\n") + content_buf.write(self.LINE_SEP) content_buf.write(self.COLUMN_STARTING_CHAR) for i, header in enumerate(headers): if i + 1 == column_count: @@ -99,12 +101,12 @@ class PleasantTable(object): else: content_buf.write(headers[i]) content_buf.write(self.COLUMN_SEPARATOR_CHAR) - content_buf.write("\n") + content_buf.write(self.LINE_SEP) content_buf.write(header_footer_buf.getvalue()) # Build the main content. row_count = len(self._rows) if row_count: - content_buf.write("\n") + content_buf.write(self.LINE_SEP) for i, row in enumerate(self._rows): pieces = [] for j, column in enumerate(row): @@ -122,7 +124,7 @@ class PleasantTable(object): content_buf.write(self.COLUMN_STARTING_CHAR) content_buf.write(blob) if i + 1 != row_count: - content_buf.write("\n") - content_buf.write("\n") + content_buf.write(self.LINE_SEP) + content_buf.write(self.LINE_SEP) content_buf.write(header_footer_buf.getvalue()) return content_buf.getvalue() diff --git a/taskflow/types/tree.py b/taskflow/types/tree.py index 6eb960b40..e6fad20c6 100644 --- a/taskflow/types/tree.py +++ b/taskflow/types/tree.py @@ -16,6 +16,8 @@ # License for the specific language governing permissions and limitations # under the License. +import os + import six @@ -148,7 +150,7 @@ class Node(object): for i, line in enumerate(_inner_pformat(self, 0)): accumulator.write(line) if i < expected_lines: - accumulator.write('\n') + accumulator.write(os.linesep) return accumulator.getvalue() def child_count(self, only_direct=True): diff --git a/taskflow/utils/misc.py b/taskflow/utils/misc.py index dd3610e79..5aa62fd92 100644 --- a/taskflow/utils/misc.py +++ b/taskflow/utils/misc.py @@ -142,6 +142,11 @@ def clamp(value, minimum, maximum, on_clamped=None): return value +def fix_newlines(text, replacement=os.linesep): + """Fixes text that *may* end with wrong nl by replacing with right nl.""" + return replacement.join(text.splitlines()) + + def binary_encode(text, encoding='utf-8'): """Converts a string of into a binary type using given encoding. diff --git a/taskflow/utils/persistence_utils.py b/taskflow/utils/persistence_utils.py index b8a153514..6d5d1685d 100644 --- a/taskflow/utils/persistence_utils.py +++ b/taskflow/utils/persistence_utils.py @@ -15,6 +15,7 @@ # under the License. import contextlib +import os from oslo.utils import timeutils from oslo.utils import uuidutils @@ -139,7 +140,7 @@ def pformat_atom_detail(atom_detail, indent=0): lines.append("%s- failure = %s" % (" " * (indent + 1), bool(atom_detail.failure))) lines.extend(_format_meta(atom_detail.meta, indent=indent + 1)) - return "\n".join(lines) + return os.linesep.join(lines) def pformat_flow_detail(flow_detail, indent=0): @@ -149,7 +150,7 @@ def pformat_flow_detail(flow_detail, indent=0): lines.extend(_format_meta(flow_detail.meta, indent=indent + 1)) for task_detail in flow_detail: lines.append(pformat_atom_detail(task_detail, indent=indent + 1)) - return "\n".join(lines) + return os.linesep.join(lines) def pformat(book, indent=0): @@ -167,4 +168,4 @@ def pformat(book, indent=0): timeutils.isotime(book.updated_at))) for flow_detail in book: lines.append(pformat_flow_detail(flow_detail, indent=indent + 1)) - return "\n".join(lines) + return os.linesep.join(lines)