Skip to content

Commit 0e1172d

Browse files
committed
Merge branch 'better_exceptions'
2 parents 5f12e7b + e2bd5a8 commit 0e1172d

File tree

5 files changed

+108
-52
lines changed

5 files changed

+108
-52
lines changed

testgres/consts.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
LOGS_DIR = "logs"
66

77
RECOVERY_CONF_FILE = "recovery.conf"
8+
PG_AUTO_CONF_FILE = "postgresql.auto.conf"
89
PG_CONF_FILE = "postgresql.conf"
910
HBA_CONF_FILE = "pg_hba.conf"
1011

testgres/exceptions.py

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,95 @@
11
# coding: utf-8
22

3+
import six
34

4-
class TestgresException(Exception):
5-
"""
6-
Base exception
7-
"""
85

6+
class TestgresException(Exception):
97
pass
108

119

10+
@six.python_2_unicode_compatible
1211
class ExecUtilException(TestgresException):
13-
"""
14-
Stores exit code
15-
"""
16-
17-
def __init__(self, message, exit_code=0):
12+
def __init__(self,
13+
message=None,
14+
command=None,
15+
exit_code=0,
16+
out=None):
1817
super(ExecUtilException, self).__init__(message)
18+
19+
self.message = message
20+
self.command = command
1921
self.exit_code = exit_code
22+
self.out = out
2023

24+
def __str__(self):
25+
msg = []
2126

22-
class ClusterTestgresException(TestgresException):
23-
pass
27+
if self.message:
28+
msg.append(self.message)
29+
30+
if self.command:
31+
msg.append(u'Command: {}'.format(self.command))
32+
33+
if self.exit_code:
34+
msg.append(u'Exit code: {}'.format(self.exit_code))
2435

36+
if self.out:
37+
msg.append(u'----\n{}'.format(self.out))
2538

39+
return six.text_type('\n').join(msg)
40+
41+
42+
@six.python_2_unicode_compatible
2643
class QueryException(TestgresException):
44+
def __init__(self, message=None, query=None):
45+
super(QueryException, self).__init__(message)
46+
47+
self.message = message
48+
self.query = query
49+
50+
def __str__(self):
51+
msg = []
52+
53+
if self.message:
54+
msg.append(self.message)
55+
56+
if self.query:
57+
msg.append(u'Query: {}'.format(self.query))
58+
59+
return six.text_type('\n').join(msg)
60+
61+
62+
class TimeoutException(QueryException):
2763
pass
2864

2965

30-
class TimeoutException(TestgresException):
66+
class CatchUpException(QueryException):
3167
pass
3268

3369

70+
@six.python_2_unicode_compatible
3471
class StartNodeException(TestgresException):
35-
pass
72+
def __init__(self, message=None, files=None):
73+
super(StartNodeException, self).__init__(message)
3674

75+
self.message = message
76+
self.files = files
3777

38-
class InitNodeException(TestgresException):
39-
pass
78+
def __str__(self):
79+
msg = []
4080

81+
if self.message:
82+
msg.append(self.message)
4183

42-
class BackupException(TestgresException):
84+
for f, lines in self.files or []:
85+
msg.append(u'{}\n----\n{}\n'.format(f, lines))
86+
87+
return six.text_type('\n').join(msg)
88+
89+
90+
class InitNodeException(TestgresException):
4391
pass
4492

4593

46-
class CatchUpException(TestgresException):
94+
class BackupException(TestgresException):
4795
pass

testgres/node.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
DATA_DIR, \
2525
LOGS_DIR, \
2626
PG_CONF_FILE, \
27+
PG_AUTO_CONF_FILE, \
2728
HBA_CONF_FILE, \
2829
RECOVERY_CONF_FILE, \
2930
PG_LOG_FILE, \
@@ -213,22 +214,18 @@ def _maybe_stop_logger(self):
213214
if self._logger:
214215
self._logger.stop()
215216

216-
def _format_verbose_error(self, message=None):
217-
# list of important files + N of last lines
217+
def _collect_special_files(self):
218+
result = []
219+
220+
# list of important files + last N lines
218221
files = [
219222
(os.path.join(self.data_dir, PG_CONF_FILE), 0),
220-
(os.path.join(self.data_dir, HBA_CONF_FILE), 0),
223+
(os.path.join(self.data_dir, PG_AUTO_CONF_FILE), 0),
221224
(os.path.join(self.data_dir, RECOVERY_CONF_FILE), 0),
225+
(os.path.join(self.data_dir, HBA_CONF_FILE), 0),
222226
(self.pg_log_name, TestgresConfig.error_log_lines)
223227
]
224228

225-
error_text = ""
226-
227-
# append message if asked to
228-
if message:
229-
error_text += message
230-
error_text += '\n' * 2
231-
232229
for f, num_lines in files:
233230
# skip missing files
234231
if not os.path.exists(f):
@@ -242,10 +239,10 @@ def _format_verbose_error(self, message=None):
242239
# read whole file
243240
lines = _f.read().decode('utf-8')
244241

245-
# append contents
246-
error_text += u"{}:\n----\n{}\n".format(f, lines)
242+
# fill list
243+
result.append((f, lines))
247244

248-
return error_text
245+
return result
249246

250247
def init(self,
251248
fsync=False,
@@ -427,7 +424,8 @@ def get_pid(self):
427424
"""
428425

429426
if self.status():
430-
with io.open(os.path.join(self.data_dir, 'postmaster.pid')) as f:
427+
pid_file = os.path.join(self.data_dir, 'postmaster.pid')
428+
with io.open(pid_file) as f:
431429
return int(f.readline())
432430

433431
# for clarity
@@ -476,8 +474,9 @@ def start(self, params=[]):
476474
try:
477475
execute_utility(_params, self.utils_log_name)
478476
except ExecUtilException as e:
479-
msg = self._format_verbose_error('Cannot start node')
480-
raise_from(StartNodeException(msg), e)
477+
msg = 'Cannot start node'
478+
files = self._collect_special_files()
479+
raise_from(StartNodeException(msg, files), e)
481480

482481
self._maybe_start_logger()
483482

@@ -531,8 +530,9 @@ def restart(self, params=[]):
531530
try:
532531
execute_utility(_params, self.utils_log_name)
533532
except ExecUtilException as e:
534-
msg = self._format_verbose_error('Cannot restart node')
535-
raise_from(StartNodeException(msg), e)
533+
msg = 'Cannot restart node'
534+
files = self._collect_special_files()
535+
raise_from(StartNodeException(msg, files), e)
536536

537537
self._maybe_start_logger()
538538

@@ -693,7 +693,7 @@ def safe_psql(self,
693693
username=username,
694694
input=input)
695695
if ret:
696-
raise QueryException((err or b'').decode('utf-8'))
696+
raise QueryException((err or b'').decode('utf-8'), query)
697697

698698
return out
699699

@@ -791,13 +791,13 @@ def poll_query_until(self,
791791
return # done
792792

793793
if res is None:
794-
raise QueryException('Query returned None')
794+
raise QueryException('Query returned None', query)
795795

796796
if len(res) == 0:
797-
raise QueryException('Query returned 0 rows')
797+
raise QueryException('Query returned 0 rows', query)
798798

799799
if len(res[0]) == 0:
800-
raise QueryException('Query returned 0 columns')
800+
raise QueryException('Query returned 0 columns', query)
801801

802802
if res[0][0] == expected:
803803
return # done
@@ -910,7 +910,7 @@ def catchup(self, dbname=None, username=None):
910910
username=username,
911911
max_attempts=0) # infinite
912912
except Exception as e:
913-
raise_from(CatchUpException('Failed to catch up'), e)
913+
raise_from(CatchUpException("Failed to catch up", poll_lsn), e)
914914

915915
def pgbench(self,
916916
dbname=None,

testgres/utils.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -90,29 +90,31 @@ def execute_utility(args, logfile):
9090
out, _ = process.communicate()
9191
out = '' if not out else out.decode('utf-8')
9292

93+
# format command
94+
command = u' '.join(args)
95+
9396
# write new log entry if possible
9497
try:
9598
with io.open(logfile, 'a') as file_out:
96-
# write util's name and args
97-
file_out.write(u' '.join(args))
99+
file_out.write(command)
98100

99-
# write output
100101
if out:
102+
# comment-out lines
103+
lines = ('# ' + l for l in out.splitlines(True))
101104
file_out.write(u'\n')
102-
file_out.write(out)
105+
file_out.writelines(lines)
103106

104-
# finally, a separator
105107
file_out.write(u'\n')
106108
except IOError:
107109
pass
108110

109-
# format exception, if needed
110-
error_code = process.returncode
111-
if error_code:
112-
error_text = (u"{} failed with exit code {}\n"
113-
u"log:\n----\n{}\n").format(args[0], error_code, out)
114-
115-
raise ExecUtilException(error_text, error_code)
111+
exit_code = process.returncode
112+
if exit_code:
113+
message = 'Utility exited with non-zero code'
114+
raise ExecUtilException(message=message,
115+
command=command,
116+
exit_code=exit_code,
117+
out=out)
116118

117119
return out
118120

tests/test_simple.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,11 @@ def test_ports_management(self):
620620
# check that port has been freed successfully
621621
self.assertEqual(len(bound_ports), 0)
622622

623+
def test_exceptions(self):
624+
str(StartNodeException('msg', [('file', 'lines')]))
625+
str(ExecUtilException('msg', 'cmd', 1, 'out'))
626+
str(QueryException('msg', 'query'))
627+
623628
def test_version_management(self):
624629
a = LooseVersion('10.0')
625630
b = LooseVersion('10')

0 commit comments

Comments
 (0)