mirror of
https://github.com/outbackdingo/patroni.git
synced 2026-01-27 10:20:10 +00:00
Switch to texttable
it seems to be well maintained and packages are available even for old distros.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Patroni Control
|
||||
'''
|
||||
@@ -32,7 +33,7 @@ from patroni.utils import cluster_as_json, patch_config, polling_loop
|
||||
from patroni.request import PatroniRequest
|
||||
from patroni.version import __version__
|
||||
from six.moves.urllib_parse import urlparse
|
||||
from terminaltables import SingleTable
|
||||
from texttable import Texttable
|
||||
|
||||
CONFIG_DIR_PATH = click.get_app_dir('patroni')
|
||||
CONFIG_FILE_PATH = os.path.join(CONFIG_DIR_PATH, 'patronictl.yaml')
|
||||
@@ -46,6 +47,44 @@ class PatroniCtlException(ClickException):
|
||||
pass
|
||||
|
||||
|
||||
class PatronictlTexttable(Texttable):
|
||||
|
||||
def __init__(self, header=None):
|
||||
Texttable.__init__(self, 0)
|
||||
self.__table_header = header
|
||||
self.__hline_num = 0
|
||||
|
||||
if sys.platform != 'win32':
|
||||
self._char_horiz = u'─'
|
||||
self._char_vert = u'│'
|
||||
self._hline_header = self._hline
|
||||
self._char_header = self._char_horiz
|
||||
|
||||
def _is_first_hline(self):
|
||||
return self.__hline_num == 0
|
||||
|
||||
def _is_last_hline(self):
|
||||
return self.__hline_num > (len(self._rows) if self._has_hlines() else int(bool(self._header)))
|
||||
|
||||
def _hline(self):
|
||||
if sys.platform == 'win32':
|
||||
left = right = self._char_corner
|
||||
elif self._is_first_hline():
|
||||
left, self._char_corner, right = u'┌', u'┬', u'┐'
|
||||
elif not self._is_last_hline():
|
||||
left, self._char_corner, right = u'├', u'┼', u'┤'
|
||||
else:
|
||||
left, self._char_corner, right = u'└', u'┴', u'┘'
|
||||
|
||||
line = self._build_hline()
|
||||
|
||||
if self._is_first_hline() and self.__table_header:
|
||||
left += self.__table_header
|
||||
|
||||
self.__hline_num += 1
|
||||
return left + line[len(left):-2] + right + '\n'
|
||||
|
||||
|
||||
def parse_dcs(dcs):
|
||||
if dcs is None:
|
||||
return None
|
||||
@@ -145,20 +184,25 @@ def print_output(columns, rows, alignment=None, fmt='pretty', header=None, delim
|
||||
for row in rows:
|
||||
if row[i]:
|
||||
row[i] = format_config_for_editing(row[i], fmt == 'tsv').strip()
|
||||
s = int(list_cluster and fmt == 'pretty') # skip cluster name if pretty-printing
|
||||
table_data = ([columns[s:]] if columns else []) + [row[s:] for row in rows]
|
||||
if list_cluster and fmt == 'pretty': # skip cluster name if pretty-printing
|
||||
columns = columns[1:] if columns else []
|
||||
rows = [row[1:] for row in rows]
|
||||
|
||||
if fmt == 'tsv':
|
||||
for r in table_data:
|
||||
for r in [columns] + rows:
|
||||
click.echo(delimiter.join(map(str, r)))
|
||||
else:
|
||||
table = SingleTable(table_data, header)
|
||||
table.inner_row_border = any(any(isinstance(c, six.string_types) and '\n' in c for c in r) for r in rows)
|
||||
for i, name in enumerate(table_data[0]):
|
||||
default = 'left'
|
||||
jmap = {m[0]: m for m in ('center', default, 'right')}
|
||||
table.justify_columns[i] = jmap.get((alignment or {}).get(name, default)[0], default)
|
||||
click.echo(table.table)
|
||||
table = PatronictlTexttable(header)
|
||||
if not any(any(isinstance(c, six.string_types) and '\n' in c for c in r) for r in rows):
|
||||
table.set_deco(Texttable.VLINES | Texttable.BORDER | Texttable.HEADER)
|
||||
if rows:
|
||||
if columns:
|
||||
table.header(columns)
|
||||
table.set_cols_align([(alignment or {}).get(c, 'l') for c in columns])
|
||||
table.add_rows(rows, header=False)
|
||||
else:
|
||||
table.add_rows([columns], header=False)
|
||||
click.echo(table.draw())
|
||||
|
||||
|
||||
def watching(w, watch, max_count=None, clear=True):
|
||||
|
||||
@@ -6,7 +6,7 @@ kazoo>=1.3.1
|
||||
python-etcd>=0.4.3,<0.5
|
||||
python-consul>=0.7.1
|
||||
click>=4.1
|
||||
terminaltables>=3.1.0
|
||||
texttable
|
||||
python-dateutil
|
||||
psutil>=2.0.0
|
||||
cdiff
|
||||
|
||||
@@ -7,7 +7,7 @@ from datetime import datetime, timedelta
|
||||
from mock import patch, Mock
|
||||
from patroni.ctl import ctl, store_config, load_config, output_members, get_dcs, parse_dcs, \
|
||||
get_all_members, get_any_member, get_cursor, query_member, configure, PatroniCtlException, apply_config_changes, \
|
||||
format_config_for_editing, show_diff, invoke_editor, format_pg_version, find_executable
|
||||
format_config_for_editing, show_diff, invoke_editor, format_pg_version, find_executable, print_output
|
||||
from patroni.dcs.etcd import Client, Failover
|
||||
from patroni.utils import tzutc
|
||||
from psycopg2 import OperationalError
|
||||
@@ -32,7 +32,8 @@ def test_rw_config():
|
||||
|
||||
|
||||
@patch('patroni.ctl.load_config',
|
||||
Mock(return_value={'scope': 'alpha', 'postgresql': {'data_dir': '.', 'pgpass': './pgpass', 'parameters': {}, 'retry_timeout': 5},
|
||||
Mock(return_value={'scope': 'alpha', 'postgresql': {'data_dir': '.', 'pgpass': './pgpass',
|
||||
'parameters': {}, 'retry_timeout': 5},
|
||||
'restapi': {'listen': '::', 'certfile': 'a'}, 'etcd': {'host': 'localhost:2379'}}))
|
||||
class TestCtl(unittest.TestCase):
|
||||
|
||||
@@ -161,6 +162,12 @@ class TestCtl(unittest.TestCase):
|
||||
def test_get_dcs(self):
|
||||
self.assertRaises(PatroniCtlException, get_dcs, {'dummy': {}}, 'dummy')
|
||||
|
||||
@patch('sys.platform', 'win32')
|
||||
@patch('click.echo')
|
||||
def test_print_output(self, mock_click_echo):
|
||||
print_output(['a'], [])
|
||||
mock_click_echo.assert_called_once_with('+---+\n| a |\n+---+')
|
||||
|
||||
@patch('psycopg2.connect', psycopg2_connect)
|
||||
@patch('patroni.ctl.query_member', Mock(return_value=([['mock column']], None)))
|
||||
@patch('patroni.ctl.get_dcs')
|
||||
|
||||
Reference in New Issue
Block a user