Merge "Remove the dependency on prettytable"
This commit is contained in:
commit
9d35f057ba
@ -18,5 +18,3 @@ stevedore>=0.14
|
||||
futures>=2.1.6
|
||||
# Used for structured input validation
|
||||
jsonschema>=2.0.0,<3.0.0
|
||||
# For pretty printing state-machine tables
|
||||
PrettyTable>=0.7,<0.8
|
||||
|
@ -14,5 +14,3 @@ Babel>=1.3
|
||||
stevedore>=1.0.0 # Apache-2.0
|
||||
# Used for structured input validation
|
||||
jsonschema>=2.0.0,<3.0.0
|
||||
# For pretty printing state-machine tables
|
||||
PrettyTable>=0.7,<0.8
|
||||
|
@ -47,23 +47,23 @@ class _MachineBuilder(object):
|
||||
NOTE(harlowja): the machine states that this build will for are::
|
||||
|
||||
+--------------+-----------+------------+----------+---------+
|
||||
| Start | Event | End | On Enter | On Exit |
|
||||
Start | Event | End | On Enter | On Exit
|
||||
+--------------+-----------+------------+----------+---------+
|
||||
| ANALYZING | finished | GAME_OVER | on_enter | on_exit |
|
||||
| ANALYZING | schedule | SCHEDULING | on_enter | on_exit |
|
||||
| ANALYZING | wait | WAITING | on_enter | on_exit |
|
||||
| FAILURE[$] | | | | |
|
||||
| GAME_OVER | failed | FAILURE | on_enter | on_exit |
|
||||
| GAME_OVER | reverted | REVERTED | on_enter | on_exit |
|
||||
| GAME_OVER | success | SUCCESS | on_enter | on_exit |
|
||||
| GAME_OVER | suspended | SUSPENDED | on_enter | on_exit |
|
||||
| RESUMING | schedule | SCHEDULING | on_enter | on_exit |
|
||||
| REVERTED[$] | | | | |
|
||||
| SCHEDULING | wait | WAITING | on_enter | on_exit |
|
||||
| SUCCESS[$] | | | | |
|
||||
| SUSPENDED[$] | | | | |
|
||||
| UNDEFINED[^] | start | RESUMING | on_enter | on_exit |
|
||||
| WAITING | analyze | ANALYZING | on_enter | on_exit |
|
||||
ANALYZING | finished | GAME_OVER | |
|
||||
ANALYZING | schedule | SCHEDULING | |
|
||||
ANALYZING | wait | WAITING | |
|
||||
FAILURE[$] | | | |
|
||||
GAME_OVER | failed | FAILURE | |
|
||||
GAME_OVER | reverted | REVERTED | |
|
||||
GAME_OVER | success | SUCCESS | |
|
||||
GAME_OVER | suspended | SUSPENDED | |
|
||||
RESUMING | schedule | SCHEDULING | |
|
||||
REVERTED[$] | | | |
|
||||
SCHEDULING | wait | WAITING | |
|
||||
SUCCESS[$] | | | |
|
||||
SUSPENDED[$] | | | |
|
||||
UNDEFINED[^] | start | RESUMING | |
|
||||
WAITING | analyze | ANALYZING | |
|
||||
+--------------+-----------+------------+----------+---------+
|
||||
|
||||
Between any of these yielded states (minus ``GAME_OVER`` and ``UNDEFINED``)
|
||||
|
@ -23,6 +23,7 @@ from taskflow import exceptions as excp
|
||||
from taskflow import test
|
||||
from taskflow.types import fsm
|
||||
from taskflow.types import graph
|
||||
from taskflow.types import table
|
||||
from taskflow.types import timing as tt
|
||||
from taskflow.types import tree
|
||||
|
||||
@ -161,6 +162,26 @@ class StopWatchTest(test.TestCase):
|
||||
self.assertGreater(0.01, watch.elapsed())
|
||||
|
||||
|
||||
class TableTest(test.TestCase):
|
||||
def test_create_valid_no_rows(self):
|
||||
tbl = table.PleasantTable(['Name', 'City', 'State', 'Country'])
|
||||
self.assertGreater(0, len(tbl.pformat()))
|
||||
|
||||
def test_create_valid_rows(self):
|
||||
tbl = table.PleasantTable(['Name', 'City', 'State', 'Country'])
|
||||
before_rows = tbl.pformat()
|
||||
tbl.add_row(["Josh", "San Jose", "CA", "USA"])
|
||||
after_rows = tbl.pformat()
|
||||
self.assertGreater(len(before_rows), len(after_rows))
|
||||
|
||||
def test_create_invalid_columns(self):
|
||||
self.assertRaises(ValueError, table.PleasantTable, [])
|
||||
|
||||
def test_create_invalid_rows(self):
|
||||
tbl = table.PleasantTable(['Name', 'City', 'State', 'Country'])
|
||||
self.assertRaises(ValueError, tbl.add_row, ['a', 'b'])
|
||||
|
||||
|
||||
class FSMTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(FSMTest, self).setUp()
|
||||
|
@ -19,10 +19,10 @@ try:
|
||||
except ImportError:
|
||||
from ordereddict import OrderedDict # noqa
|
||||
|
||||
import prettytable
|
||||
import six
|
||||
|
||||
from taskflow import exceptions as excp
|
||||
from taskflow.types import table
|
||||
|
||||
|
||||
class _Jump(object):
|
||||
@ -252,8 +252,8 @@ class FSM(object):
|
||||
if sort:
|
||||
return sorted(six.iterkeys(data))
|
||||
return list(six.iterkeys(data))
|
||||
tbl = prettytable.PrettyTable(
|
||||
["Start", "Event", "End", "On Enter", "On Exit"])
|
||||
tbl = table.PleasantTable(["Start", "Event", "End",
|
||||
"On Enter", "On Exit"])
|
||||
for state in orderedkeys(self._states):
|
||||
prefix_markings = []
|
||||
if self.current_state == state:
|
||||
@ -287,4 +287,4 @@ class FSM(object):
|
||||
tbl.add_row(row)
|
||||
else:
|
||||
tbl.add_row([pretty_state, "", "", "", ""])
|
||||
return tbl.get_string(print_empty=True)
|
||||
return tbl.pformat()
|
||||
|
128
taskflow/types/table.py
Normal file
128
taskflow/types/table.py
Normal file
@ -0,0 +1,128 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2014 Yahoo! Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import itertools
|
||||
|
||||
import six
|
||||
|
||||
|
||||
class PleasantTable(object):
|
||||
"""A tiny pretty printing table (like prettytable/tabulate but smaller).
|
||||
|
||||
Creates simply formatted tables (with no special sauce)::
|
||||
|
||||
>>> from taskflow.types import table
|
||||
>>> tbl = table.PleasantTable(['Name', 'City', 'State', 'Country'])
|
||||
>>> tbl.add_row(["Josh", "San Jose", "CA", "USA"])
|
||||
>>> print(tbl.pformat())
|
||||
+------+----------+-------+---------+
|
||||
Name | City | State | Country
|
||||
+------+----------+-------+---------+
|
||||
Josh | San Jose | CA | USA
|
||||
+------+----------+-------+---------+
|
||||
"""
|
||||
COLUMN_STARTING_CHAR = ' '
|
||||
COLUMN_ENDING_CHAR = ''
|
||||
COLUMN_SEPARATOR_CHAR = '|'
|
||||
HEADER_FOOTER_JOINING_CHAR = '+'
|
||||
HEADER_FOOTER_CHAR = '-'
|
||||
|
||||
@staticmethod
|
||||
def _center_text(text, max_len, fill=' '):
|
||||
return '{0:{fill}{align}{size}}'.format(text, fill=fill,
|
||||
align="^", size=max_len)
|
||||
|
||||
@classmethod
|
||||
def _size_selector(cls, possible_sizes):
|
||||
# The number two is used so that the edges of a column have spaces
|
||||
# around them (instead of being right next to a column separator).
|
||||
try:
|
||||
return max(x + 2 for x in possible_sizes)
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
def __init__(self, columns):
|
||||
if len(columns) == 0:
|
||||
raise ValueError("Column count must be greater than zero")
|
||||
self._columns = [column.strip() for column in columns]
|
||||
self._rows = []
|
||||
|
||||
def add_row(self, row):
|
||||
if len(row) != len(self._columns):
|
||||
raise ValueError("Row must have %s columns instead of"
|
||||
" %s columns" % (len(self._columns), len(row)))
|
||||
self._rows.append([six.text_type(column) for column in row])
|
||||
|
||||
def pformat(self):
|
||||
# Figure out the maximum column sizes...
|
||||
column_count = len(self._columns)
|
||||
column_sizes = [0] * column_count
|
||||
headers = []
|
||||
for i, column in enumerate(self._columns):
|
||||
possible_sizes_iter = itertools.chain(
|
||||
[len(column)], (len(row[i]) for row in self._rows))
|
||||
column_sizes[i] = self._size_selector(possible_sizes_iter)
|
||||
headers.append(self._center_text(column, column_sizes[i]))
|
||||
# Build the header and footer prefix/postfix.
|
||||
header_footer_buf = six.StringIO()
|
||||
header_footer_buf.write(self.HEADER_FOOTER_JOINING_CHAR)
|
||||
for i, header in enumerate(headers):
|
||||
header_footer_buf.write(self.HEADER_FOOTER_CHAR * len(header))
|
||||
if i + 1 != column_count:
|
||||
header_footer_buf.write(self.HEADER_FOOTER_JOINING_CHAR)
|
||||
header_footer_buf.write(self.HEADER_FOOTER_JOINING_CHAR)
|
||||
# Build the main header.
|
||||
content_buf = six.StringIO()
|
||||
content_buf.write(header_footer_buf.getvalue())
|
||||
content_buf.write("\n")
|
||||
content_buf.write(self.COLUMN_STARTING_CHAR)
|
||||
for i, header in enumerate(headers):
|
||||
if i + 1 == column_count:
|
||||
if self.COLUMN_ENDING_CHAR:
|
||||
content_buf.write(headers[i])
|
||||
content_buf.write(self.COLUMN_ENDING_CHAR)
|
||||
else:
|
||||
content_buf.write(headers[i].rstrip())
|
||||
else:
|
||||
content_buf.write(headers[i])
|
||||
content_buf.write(self.COLUMN_SEPARATOR_CHAR)
|
||||
content_buf.write("\n")
|
||||
content_buf.write(header_footer_buf.getvalue())
|
||||
# Build the main content.
|
||||
row_count = len(self._rows)
|
||||
if row_count:
|
||||
content_buf.write("\n")
|
||||
for i, row in enumerate(self._rows):
|
||||
pieces = []
|
||||
for j, column in enumerate(row):
|
||||
pieces.append(self._center_text(column, column_sizes[j]))
|
||||
if j + 1 != column_count:
|
||||
pieces.append(self.COLUMN_SEPARATOR_CHAR)
|
||||
blob = ''.join(pieces)
|
||||
if self.COLUMN_ENDING_CHAR:
|
||||
content_buf.write(self.COLUMN_STARTING_CHAR)
|
||||
content_buf.write(blob)
|
||||
content_buf.write(self.COLUMN_ENDING_CHAR)
|
||||
else:
|
||||
blob = blob.rstrip()
|
||||
if blob:
|
||||
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(header_footer_buf.getvalue())
|
||||
return content_buf.getvalue()
|
Loading…
Reference in New Issue
Block a user