Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit f9ddd04

Browse files
Proposal to fix #154 (v2)
- The one way to generate ExecUtilException - RaiseError.UtilityExitedWithNonZeroCode - RaiseError is added - ExecUtilException::error is added (it contains the error data) - ExecUtilException::__str__ is updated - PostgresNode::psql and PostgresNode::safe_psql are updated - TestLocalOperations::test_exec_command_failure is updated - TestRemoteOperations::test_exec_command_failure is updated - TestRemoteOperations::test_makedirs_and_rmdirs_failure is updated
1 parent 1c73113 commit f9ddd04

File tree

7 files changed

+110
-47
lines changed

7 files changed

+110
-47
lines changed

testgres/exceptions.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ class TestgresException(Exception):
99

1010
@six.python_2_unicode_compatible
1111
class ExecUtilException(TestgresException):
12-
def __init__(self, message=None, command=None, exit_code=0, out=None):
12+
def __init__(self, message=None, command=None, exit_code=0, out=None, error=None):
1313
super(ExecUtilException, self).__init__(message)
1414

1515
self.message = message
1616
self.command = command
1717
self.exit_code = exit_code
1818
self.out = out
19+
self.error = error
1920

2021
def __str__(self):
2122
msg = []
@@ -24,13 +25,17 @@ def __str__(self):
2425
msg.append(self.message)
2526

2627
if self.command:
27-
msg.append(u'Command: {}'.format(self.command))
28+
command_s = ' '.join(self.command) if isinstance(self.command, list) else self.command,
29+
msg.append(u'Command: {}'.format(command_s))
2830

2931
if self.exit_code:
3032
msg.append(u'Exit code: {}'.format(self.exit_code))
3133

34+
if self.error:
35+
msg.append(u'---- Error:\n{}'.format(self.error))
36+
3237
if self.out:
33-
msg.append(u'----\n{}'.format(self.out))
38+
msg.append(u'---- Out:\n{}'.format(self.out))
3439

3540
return self.convert_and_join(msg)
3641

testgres/helpers/raise_error.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from ..exceptions import ExecUtilException
2+
3+
4+
class RaiseError:
5+
def UtilityExitedWithNonZeroCode(cmd, exit_code, msg_arg, error, out):
6+
assert type(exit_code) == int # noqa: E721
7+
8+
msg_arg_s = __class__._TranslateDataIntoString(msg_arg).strip()
9+
assert type(msg_arg_s) == str # noqa: E721
10+
11+
if msg_arg_s == "":
12+
msg_arg_s = "#no_error_message"
13+
14+
message = "Utility exited with non-zero code. Error: `" + msg_arg_s + "`"
15+
raise ExecUtilException(
16+
message=message,
17+
command=cmd,
18+
exit_code=exit_code,
19+
out=out,
20+
error=error)
21+
22+
def _TranslateDataIntoString(data):
23+
if type(data) == bytes: # noqa: E721
24+
return __class__._TranslateDataIntoString__FromBinary(data)
25+
26+
return str(data)
27+
28+
def _TranslateDataIntoString__FromBinary(data):
29+
assert type(data) == bytes # noqa: E721
30+
31+
try:
32+
return data.decode('utf-8')
33+
except UnicodeDecodeError:
34+
pass
35+
36+
return "#cannot_decode_text"
37+
38+
def _BinaryIsASCII(data):
39+
assert type(data) == bytes # noqa: E721
40+
41+
for b in data:
42+
if not (b >= 0 and b <= 127):
43+
return False
44+
45+
return True

testgres/node.py

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import os
44
import random
55
import signal
6-
import subprocess
76
import threading
87
import tempfile
98
from queue import Queue
@@ -987,6 +986,25 @@ def psql(self,
987986
>>> psql(query='select 3', ON_ERROR_STOP=1)
988987
"""
989988

989+
return self._psql(
990+
ignore_errors=True,
991+
query=query,
992+
filename=filename,
993+
dbname=dbname,
994+
username=username,
995+
input=input,
996+
**variables
997+
)
998+
999+
def _psql(
1000+
self,
1001+
ignore_errors,
1002+
query=None,
1003+
filename=None,
1004+
dbname=None,
1005+
username=None,
1006+
input=None,
1007+
**variables):
9901008
dbname = dbname or default_dbname()
9911009

9921010
psql_params = [
@@ -1017,20 +1035,8 @@ def psql(self,
10171035

10181036
# should be the last one
10191037
psql_params.append(dbname)
1020-
if not self.os_ops.remote:
1021-
# start psql process
1022-
process = subprocess.Popen(psql_params,
1023-
stdin=subprocess.PIPE,
1024-
stdout=subprocess.PIPE,
1025-
stderr=subprocess.PIPE)
1026-
1027-
# wait until it finishes and get stdout and stderr
1028-
out, err = process.communicate(input=input)
1029-
return process.returncode, out, err
1030-
else:
1031-
status_code, out, err = self.os_ops.exec_command(psql_params, verbose=True, input=input)
10321038

1033-
return status_code, out, err
1039+
return self.os_ops.exec_command(psql_params, verbose=True, input=input, ignore_errors=ignore_errors)
10341040

10351041
@method_decorator(positional_args_hack(['dbname', 'query']))
10361042
def safe_psql(self, query=None, expect_error=False, **kwargs):
@@ -1051,21 +1057,17 @@ def safe_psql(self, query=None, expect_error=False, **kwargs):
10511057
Returns:
10521058
psql's output as str.
10531059
"""
1060+
assert type(kwargs) == dict # noqa: E721
1061+
assert not ("ignore_errors" in kwargs.keys())
10541062

10551063
# force this setting
10561064
kwargs['ON_ERROR_STOP'] = 1
10571065
try:
1058-
ret, out, err = self.psql(query=query, **kwargs)
1066+
ret, out, err = self._psql(ignore_errors=False, query=query, **kwargs)
10591067
except ExecUtilException as e:
1060-
ret = e.exit_code
1061-
out = e.out
1062-
err = e.message
1063-
if ret:
1064-
if expect_error:
1065-
out = (err or b'').decode('utf-8')
1066-
else:
1067-
raise QueryException((err or b'').decode('utf-8'), query)
1068-
elif expect_error:
1068+
raise QueryException(e.message, query)
1069+
1070+
if expect_error:
10691071
assert False, "Exception was expected, but query finished successfully: `{}` ".format(query)
10701072

10711073
return out

testgres/operations/local_ops.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from ..exceptions import ExecUtilException
1313
from .os_ops import ConnectionParams, OsOperations, pglib, get_default_encoding
14+
from ..helpers.raise_error import RaiseError
1415

1516
try:
1617
from shutil import which as find_executable
@@ -47,14 +48,6 @@ def __init__(self, conn_params=None):
4748
self.remote = False
4849
self.username = conn_params.username or getpass.getuser()
4950

50-
@staticmethod
51-
def _raise_exec_exception(message, command, exit_code, output):
52-
"""Raise an ExecUtilException."""
53-
raise ExecUtilException(message=message.format(output),
54-
command=' '.join(command) if isinstance(command, list) else command,
55-
exit_code=exit_code,
56-
out=output)
57-
5851
@staticmethod
5952
def _process_output(encoding, temp_file_path):
6053
"""Process the output of a command from a temporary file."""
@@ -120,11 +113,20 @@ def exec_command(self, cmd, wait_exit=False, verbose=False, expect_error=False,
120113
"""
121114
Execute a command in a subprocess and handle the output based on the provided parameters.
122115
"""
116+
assert type(expect_error) == bool # noqa: E721
117+
assert type(ignore_errors) == bool # noqa: E721
118+
123119
process, output, error = self._run_command(cmd, shell, input, stdin, stdout, stderr, get_process, timeout, encoding)
124120
if get_process:
125121
return process
126122
if not ignore_errors and ((process.returncode != 0 or has_errors(output=output, error=error)) and not expect_error):
127-
self._raise_exec_exception('Utility exited with non-zero code. Error `{}`', cmd, process.returncode, error or output)
123+
RaiseError.UtilityExitedWithNonZeroCode(
124+
cmd=cmd,
125+
exit_code=process.returncode,
126+
msg_arg=error or output,
127+
error=error,
128+
out=output
129+
)
128130

129131
if verbose:
130132
return process.returncode, output, error

testgres/operations/remote_ops.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
raise ImportError("You must have psycopg2 or pg8000 modules installed")
1515

1616
from ..exceptions import ExecUtilException
17+
from ..helpers.raise_error import RaiseError
1718
from .os_ops import OsOperations, ConnectionParams, get_default_encoding
1819

1920
error_markers = [b'error', b'Permission denied', b'fatal', b'No such file or directory']
@@ -66,6 +67,9 @@ def exec_command(self, cmd, wait_exit=False, verbose=False, expect_error=False,
6667
Args:
6768
- cmd (str): The command to be executed.
6869
"""
70+
assert type(expect_error) == bool # noqa: E721
71+
assert type(ignore_errors) == bool # noqa: E721
72+
6973
ssh_cmd = []
7074
if isinstance(cmd, str):
7175
ssh_cmd = ['ssh', self.ssh_dest] + self.ssh_args + [cmd]
@@ -100,10 +104,12 @@ def exec_command(self, cmd, wait_exit=False, verbose=False, expect_error=False,
100104
error = error.decode(encoding)
101105

102106
if not ignore_errors and error_found and not expect_error:
103-
error = normalize_error(error)
104-
assert type(error) == str # noqa: E721
105-
message = "Utility exited with non-zero code. Error: " + error
106-
raise ExecUtilException(message=message, command=cmd, exit_code=exit_status, out=result)
107+
RaiseError.UtilityExitedWithNonZeroCode(
108+
cmd=cmd,
109+
exit_code=exit_status,
110+
msg_arg=error,
111+
error=error,
112+
out=result)
107113

108114
if verbose:
109115
return exit_status, result, error

tests/test_local.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def test_exec_command_failure(self):
3737
error = e.message
3838
break
3939
raise Exception("We wait an exception!")
40-
assert error == "Utility exited with non-zero code. Error `b'/bin/sh: 1: nonexistent_command: not found\\n'`"
40+
assert error == "Utility exited with non-zero code. Error: `/bin/sh: 1: nonexistent_command: not found`"
4141

4242
def test_exec_command_failure__expect_error(self):
4343
"""

tests/test_remote.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def test_exec_command_failure(self):
3737
error = e.message
3838
break
3939
raise Exception("We wait an exception!")
40-
assert error == b'Utility exited with non-zero code. Error: bash: line 1: nonexistent_command: command not found\n'
40+
assert error == 'Utility exited with non-zero code. Error: `bash: line 1: nonexistent_command: command not found`'
4141

4242
def test_exec_command_failure__expect_error(self):
4343
"""
@@ -98,11 +98,14 @@ def test_makedirs_and_rmdirs_failure(self):
9898
self.operations.makedirs(path)
9999

100100
# Test rmdirs
101-
try:
102-
exit_status, result, error = self.operations.rmdirs(path, verbose=True)
103-
except ExecUtilException as e:
104-
error = e.message
105-
assert error == b"Utility exited with non-zero code. Error: rm: cannot remove '/root/test_dir': Permission denied\n"
101+
while True:
102+
try:
103+
self.operations.rmdirs(path, verbose=True)
104+
except ExecUtilException as e:
105+
error = e.message
106+
break
107+
raise Exception("We wait an exception!")
108+
assert error == "Utility exited with non-zero code. Error: `rm: cannot remove '/root/test_dir': Permission denied`"
106109

107110
def test_listdir(self):
108111
"""

0 commit comments

Comments
 (0)