Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitb949eb8

Browse files
Merge pull request#161 from dmitry-lipetsk/master-fix154--v02
Proposal tofix#154 (v2)
2 parents1c73113 +cd0b5f8 commitb949eb8

File tree

11 files changed

+267
-58
lines changed

11 files changed

+267
-58
lines changed

‎testgres/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
CatchUpException, \
2424
StartNodeException, \
2525
InitNodeException, \
26-
BackupException
26+
BackupException, \
27+
InvalidOperationException
2728

2829
from .enumsimport \
2930
XLogMethod, \
@@ -60,7 +61,7 @@
6061
"NodeBackup","testgres_config",
6162
"TestgresConfig","configure_testgres","scoped_config","push_config","pop_config",
6263
"NodeConnection","DatabaseError","InternalError","ProgrammingError","OperationalError",
63-
"TestgresException","ExecUtilException","QueryException","TimeoutException","CatchUpException","StartNodeException","InitNodeException","BackupException",
64+
"TestgresException","ExecUtilException","QueryException","TimeoutException","CatchUpException","StartNodeException","InitNodeException","BackupException","InvalidOperationException",
6465
"XLogMethod","IsolationLevel","NodeStatus","ProcessType","DumpFormat",
6566
"PostgresNode","NodeApp",
6667
"reserve_port","release_port","bound_ports","get_bin_path","get_pg_config","get_pg_version",

‎testgres/exceptions.py

Lines changed: 12 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
classExecUtilException(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
ifself.command:
27-
msg.append(u'Command: {}'.format(self.command))
28+
command_s=' '.join(self.command)ifisinstance(self.command,list)elseself.command,
29+
msg.append(u'Command: {}'.format(command_s))
2830

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

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

3540
returnself.convert_and_join(msg)
3641

@@ -98,3 +103,7 @@ class InitNodeException(TestgresException):
98103

99104
classBackupException(TestgresException):
100105
pass
106+
107+
108+
classInvalidOperationException(TestgresException):
109+
pass

‎testgres/node.py

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@
7474
TimeoutException, \
7575
InitNodeException, \
7676
TestgresException, \
77-
BackupException
77+
BackupException, \
78+
InvalidOperationException
7879

7980
from .loggerimportTestgresLogger
8081

@@ -987,6 +988,37 @@ def psql(self,
987988
>>> psql(query='select 3', ON_ERROR_STOP=1)
988989
"""
989990

991+
returnself._psql(
992+
ignore_errors=True,
993+
query=query,
994+
filename=filename,
995+
dbname=dbname,
996+
username=username,
997+
input=input,
998+
**variables
999+
)
1000+
1001+
def_psql(
1002+
self,
1003+
ignore_errors,
1004+
query=None,
1005+
filename=None,
1006+
dbname=None,
1007+
username=None,
1008+
input=None,
1009+
**variables):
1010+
asserttype(variables)==dict# noqa: E721
1011+
1012+
#
1013+
# We do not support encoding. It may be added later. Ok?
1014+
#
1015+
ifinputisNone:
1016+
pass
1017+
eliftype(input)==bytes:# noqa: E721
1018+
pass
1019+
else:
1020+
raiseException("Input data must be None or bytes.")
1021+
9901022
dbname=dbnameordefault_dbname()
9911023

9921024
psql_params= [
@@ -1017,20 +1049,14 @@ def psql(self,
10171049

10181050
# should be the last one
10191051
psql_params.append(dbname)
1020-
ifnotself.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-
returnprocess.returncode,out,err
1030-
else:
1031-
status_code,out,err=self.os_ops.exec_command(psql_params,verbose=True,input=input)
10321052

1033-
returnstatus_code,out,err
1053+
returnself.os_ops.exec_command(
1054+
psql_params,
1055+
verbose=True,
1056+
input=input,
1057+
stderr=subprocess.PIPE,
1058+
stdout=subprocess.PIPE,
1059+
ignore_errors=ignore_errors)
10341060

10351061
@method_decorator(positional_args_hack(['dbname','query']))
10361062
defsafe_psql(self,query=None,expect_error=False,**kwargs):
@@ -1051,22 +1077,27 @@ def safe_psql(self, query=None, expect_error=False, **kwargs):
10511077
Returns:
10521078
psql's output as str.
10531079
"""
1080+
asserttype(kwargs)==dict# noqa: E721
1081+
assertnot ("ignore_errors"inkwargs.keys())
1082+
assertnot ("expect_error"inkwargs.keys())
10541083

10551084
# force this setting
10561085
kwargs['ON_ERROR_STOP']=1
10571086
try:
1058-
ret,out,err=self.psql(query=query,**kwargs)
1087+
ret,out,err=self._psql(ignore_errors=False,query=query,**kwargs)
10591088
exceptExecUtilExceptionase:
1060-
ret=e.exit_code
1061-
out=e.out
1062-
err=e.message
1063-
ifret:
1064-
ifexpect_error:
1065-
out= (errorb'').decode('utf-8')
1066-
else:
1067-
raiseQueryException((errorb'').decode('utf-8'),query)
1068-
elifexpect_error:
1069-
assertFalse,"Exception was expected, but query finished successfully: `{}` ".format(query)
1089+
ifnotexpect_error:
1090+
raiseQueryException(e.message,query)
1091+
1092+
iftype(e.error)==bytes:# noqa: E721
1093+
returne.error.decode("utf-8")# throw
1094+
1095+
# [2024-12-09] This situation is not expected
1096+
assertFalse
1097+
returne.error
1098+
1099+
ifexpect_error:
1100+
raiseInvalidOperationException("Exception was expected, but query finished successfully: `{}`.".format(query))
10701101

10711102
returnout
10721103

‎testgres/operations/helpers.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
importlocale
2+
3+
4+
classHelpers:
5+
def_make_get_default_encoding_func():
6+
# locale.getencoding is added in Python 3.11
7+
ifhasattr(locale,'getencoding'):
8+
returnlocale.getencoding
9+
10+
# It must exist
11+
returnlocale.getpreferredencoding
12+
13+
# Prepared pointer on function to get a name of system codepage
14+
_get_default_encoding_func=_make_get_default_encoding_func()
15+
16+
defGetDefaultEncoding():
17+
#
18+
# Original idea/source was:
19+
#
20+
# def os_ops.get_default_encoding():
21+
# if not hasattr(locale, 'getencoding'):
22+
# locale.getencoding = locale.getpreferredencoding
23+
# return locale.getencoding() or 'UTF-8'
24+
#
25+
26+
assert__class__._get_default_encoding_funcisnotNone
27+
28+
r=__class__._get_default_encoding_func()
29+
30+
ifr:
31+
assertrisnotNone
32+
asserttype(r)==str# noqa: E721
33+
assertr!=""
34+
returnr
35+
36+
# Is it an unexpected situation?
37+
return'UTF-8'
38+
39+
defPrepareProcessInput(input,encoding):
40+
ifnotinput:
41+
returnNone
42+
43+
iftype(input)==str:# noqa: E721
44+
ifencodingisNone:
45+
returninput.encode(__class__.GetDefaultEncoding())
46+
47+
asserttype(encoding)==str# noqa: E721
48+
returninput.encode(encoding)
49+
50+
# It is expected!
51+
asserttype(input)==bytes# noqa: E721
52+
returninput

‎testgres/operations/local_ops.py

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
from ..exceptionsimportExecUtilException
1313
from .os_opsimportConnectionParams,OsOperations,pglib,get_default_encoding
14+
from .raise_errorimportRaiseError
15+
from .helpersimportHelpers
1416

1517
try:
1618
fromshutilimportwhichasfind_executable
@@ -47,14 +49,6 @@ def __init__(self, conn_params=None):
4749
self.remote=False
4850
self.username=conn_params.usernameorgetpass.getuser()
4951

50-
@staticmethod
51-
def_raise_exec_exception(message,command,exit_code,output):
52-
"""Raise an ExecUtilException."""
53-
raiseExecUtilException(message=message.format(output),
54-
command=' '.join(command)ifisinstance(command,list)elsecommand,
55-
exit_code=exit_code,
56-
out=output)
57-
5852
@staticmethod
5953
def_process_output(encoding,temp_file_path):
6054
"""Process the output of a command from a temporary file."""
@@ -65,6 +59,8 @@ def _process_output(encoding, temp_file_path):
6559
returnoutput,None# In Windows stderr writing in stdout
6660

6761
def_run_command__nt(self,cmd,shell,input,stdin,stdout,stderr,get_process,timeout,encoding):
62+
# TODO: why don't we use the data from input?
63+
6864
withtempfile.NamedTemporaryFile(mode='w+b',delete=False)astemp_file:
6965
stdout=temp_file
7066
stderr=subprocess.STDOUT
@@ -86,25 +82,36 @@ def _run_command__nt(self, cmd, shell, input, stdin, stdout, stderr, get_process
8682
returnprocess,output,error
8783

8884
def_run_command__generic(self,cmd,shell,input,stdin,stdout,stderr,get_process,timeout,encoding):
85+
input_prepared=None
86+
ifnotget_process:
87+
input_prepared=Helpers.PrepareProcessInput(input,encoding)# throw
88+
89+
assertinput_preparedisNoneor (type(input_prepared)==bytes)# noqa: E721
90+
8991
process=subprocess.Popen(
9092
cmd,
9193
shell=shell,
9294
stdin=stdinorsubprocess.PIPEifinputisnotNoneelseNone,
9395
stdout=stdoutorsubprocess.PIPE,
9496
stderr=stderrorsubprocess.PIPE,
9597
)
98+
assertnot (processisNone)
9699
ifget_process:
97100
returnprocess,None,None
98101
try:
99-
output,error=process.communicate(input=input.encode(encoding)ifinputelseNone,timeout=timeout)
100-
ifencoding:
101-
output=output.decode(encoding)
102-
error=error.decode(encoding)
103-
returnprocess,output,error
102+
output,error=process.communicate(input=input_prepared,timeout=timeout)
104103
exceptsubprocess.TimeoutExpired:
105104
process.kill()
106105
raiseExecUtilException("Command timed out after {} seconds.".format(timeout))
107106

107+
asserttype(output)==bytes# noqa: E721
108+
asserttype(error)==bytes# noqa: E721
109+
110+
ifencoding:
111+
output=output.decode(encoding)
112+
error=error.decode(encoding)
113+
returnprocess,output,error
114+
108115
def_run_command(self,cmd,shell,input,stdin,stdout,stderr,get_process,timeout,encoding):
109116
"""Execute a command and return the process and its output."""
110117
ifos.name=='nt'andstdoutisNone:# Windows
@@ -120,11 +127,20 @@ def exec_command(self, cmd, wait_exit=False, verbose=False, expect_error=False,
120127
"""
121128
Execute a command in a subprocess and handle the output based on the provided parameters.
122129
"""
130+
asserttype(expect_error)==bool# noqa: E721
131+
asserttype(ignore_errors)==bool# noqa: E721
132+
123133
process,output,error=self._run_command(cmd,shell,input,stdin,stdout,stderr,get_process,timeout,encoding)
124134
ifget_process:
125135
returnprocess
126136
ifnotignore_errorsand ((process.returncode!=0orhas_errors(output=output,error=error))andnotexpect_error):
127-
self._raise_exec_exception('Utility exited with non-zero code. Error `{}`',cmd,process.returncode,errororoutput)
137+
RaiseError.UtilityExitedWithNonZeroCode(
138+
cmd=cmd,
139+
exit_code=process.returncode,
140+
msg_arg=errororoutput,
141+
error=error,
142+
out=output
143+
)
128144

129145
ifverbose:
130146
returnprocess.returncode,output,error

‎testgres/operations/raise_error.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from ..exceptionsimportExecUtilException
2+
from .helpersimportHelpers
3+
4+
5+
classRaiseError:
6+
defUtilityExitedWithNonZeroCode(cmd,exit_code,msg_arg,error,out):
7+
asserttype(exit_code)==int# noqa: E721
8+
9+
msg_arg_s=__class__._TranslateDataIntoString(msg_arg).strip()
10+
asserttype(msg_arg_s)==str# noqa: E721
11+
12+
ifmsg_arg_s=="":
13+
msg_arg_s="#no_error_message"
14+
15+
message="Utility exited with non-zero code. Error: `"+msg_arg_s+"`"
16+
raiseExecUtilException(
17+
message=message,
18+
command=cmd,
19+
exit_code=exit_code,
20+
out=out,
21+
error=error)
22+
23+
def_TranslateDataIntoString(data):
24+
iftype(data)==bytes:# noqa: E721
25+
return__class__._TranslateDataIntoString__FromBinary(data)
26+
27+
returnstr(data)
28+
29+
def_TranslateDataIntoString__FromBinary(data):
30+
asserttype(data)==bytes# noqa: E721
31+
32+
try:
33+
returndata.decode(Helpers.GetDefaultEncoding())
34+
exceptUnicodeDecodeError:
35+
pass
36+
37+
return"#cannot_decode_text"
38+
39+
def_BinaryIsASCII(data):
40+
asserttype(data)==bytes# noqa: E721
41+
42+
forbindata:
43+
ifnot (b>=0andb<=127):
44+
returnFalse
45+
46+
returnTrue

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp