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

Commitbdc264e

Browse files
author
vshepard
committed
Fix initdb error on Windows
1 parent846c05f commitbdc264e

File tree

5 files changed

+170
-37
lines changed

5 files changed

+170
-37
lines changed

‎testgres/operations/local_ops.py

Lines changed: 84 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
importpsutil
99

1010
from ..exceptionsimportExecUtilException
11-
from .os_opsimportConnectionParams,OsOperations
12-
from .os_opsimportpglib
11+
from .os_opsimportConnectionParams,OsOperations,pglib,get_default_encoding
1312

1413
try:
1514
fromshutilimportwhichasfind_executable
@@ -22,6 +21,12 @@
2221
error_markers= [b'error',b'Permission denied',b'fatal']
2322

2423

24+
defhas_errors(output):
25+
ifisinstance(output,str):
26+
output=output.encode(get_default_encoding())
27+
returnany(markerinoutputformarkerinerror_markers)
28+
29+
2530
classLocalOperations(OsOperations):
2631
def__init__(self,conn_params=None):
2732
ifconn_paramsisNone:
@@ -33,7 +38,38 @@ def __init__(self, conn_params=None):
3338
self.remote=False
3439
self.username=conn_params.usernameorself.get_user()
3540

36-
# Command execution
41+
@staticmethod
42+
def_run_command(cmd,shell,input,timeout,encoding,temp_file=None):
43+
"""Execute a command and return the process."""
44+
iftemp_fileisnotNone:
45+
stdout=temp_file
46+
stderr=subprocess.STDOUT
47+
else:
48+
stdout=subprocess.PIPE
49+
stderr=subprocess.PIPE
50+
51+
process=subprocess.Popen(
52+
cmd,
53+
shell=shell,
54+
stdin=subprocess.PIPEifinputisnotNoneelseNone,
55+
stdout=stdout,
56+
stderr=stderr,
57+
)
58+
59+
try:
60+
returnprocess.communicate(input=input.encode(encoding)ifinputelseNone,timeout=timeout),process
61+
exceptsubprocess.TimeoutExpired:
62+
process.kill()
63+
raiseExecUtilException("Command timed out after {} seconds.".format(timeout))
64+
65+
@staticmethod
66+
def_raise_exec_exception(message,command,exit_code,output):
67+
"""Raise an ExecUtilException."""
68+
raiseExecUtilException(message=message.format(output),
69+
command=command,
70+
exit_code=exit_code,
71+
out=output)
72+
3773
defexec_command(self,cmd,wait_exit=False,verbose=False,
3874
expect_error=False,encoding=None,shell=False,text=False,
3975
input=None,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,
@@ -56,16 +92,15 @@ def exec_command(self, cmd, wait_exit=False, verbose=False,
5692
:return: The output of the subprocess.
5793
"""
5894
ifos.name=='nt':
59-
withtempfile.NamedTemporaryFile()asbuf:
60-
process=subprocess.Popen(cmd,stdout=buf,stderr=subprocess.STDOUT)
61-
process.communicate()
62-
buf.seek(0)
63-
result=buf.read().decode(encoding)
64-
returnresult
95+
self._exec_command_windows(cmd,wait_exit=wait_exit,verbose=verbose,
96+
expect_error=expect_error,encoding=encoding,shell=shell,text=text,
97+
input=input,stdin=stdin,stdout=stdout,stderr=stderr,
98+
get_process=get_process,timeout=timeout)
6599
else:
66100
process=subprocess.Popen(
67101
cmd,
68102
shell=shell,
103+
stdin=stdin,
69104
stdout=stdout,
70105
stderr=stderr,
71106
)
@@ -79,7 +114,7 @@ def exec_command(self, cmd, wait_exit=False, verbose=False,
79114
raiseExecUtilException("Command timed out after {} seconds.".format(timeout))
80115
exit_status=process.returncode
81116

82-
error_found=exit_status!=0orany(markerinerrorformarkerinerror_markers)
117+
error_found=exit_status!=0orhas_errors(error)
83118

84119
ifencoding:
85120
result=result.decode(encoding)
@@ -91,15 +126,49 @@ def exec_command(self, cmd, wait_exit=False, verbose=False,
91126
ifexit_status!=0orerror_found:
92127
ifexit_status==0:
93128
exit_status=1
94-
raiseExecUtilException(message='Utility exited with non-zero code. Error `{}`'.format(error),
95-
command=cmd,
96-
exit_code=exit_status,
97-
out=result)
129+
self._raise_exec_exception('Utility exited with non-zero code. Error `{}`',cmd,exit_status,result)
98130
ifverbose:
99131
returnexit_status,result,error
100132
else:
101133
returnresult
102134

135+
@staticmethod
136+
def_process_output(process,encoding,temp_file=None):
137+
"""Process the output of a command."""
138+
iftemp_fileisnotNone:
139+
temp_file.seek(0)
140+
output=temp_file.read()
141+
else:
142+
output=process.stdout.read()
143+
144+
ifencoding:
145+
output=output.decode(encoding)
146+
147+
returnoutput
148+
def_exec_command_windows(self,cmd,wait_exit=False,verbose=False,
149+
expect_error=False,encoding=None,shell=False,text=False,
150+
input=None,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,
151+
get_process=None,timeout=None):
152+
withtempfile.NamedTemporaryFile(mode='w+b')astemp_file:
153+
_,process=self._run_command(cmd,shell,input,timeout,encoding,temp_file)
154+
ifget_process:
155+
returnprocess
156+
output=self._process_output(process,encoding,temp_file)
157+
158+
ifprocess.returncode!=0orhas_errors(output):
159+
ifprocess.returncode==0:
160+
process.returncode=1
161+
ifexpect_error:
162+
ifverbose:
163+
returnprocess.returncode,output,output
164+
else:
165+
returnoutput
166+
else:
167+
self._raise_exec_exception('Utility exited with non-zero code. Error `{}`',cmd,process.returncode,
168+
output)
169+
170+
return (process.returncode,output,output)ifverboseelseoutput
171+
103172
# Environment setup
104173
defenviron(self,var_name):
105174
returnos.environ.get(var_name)
@@ -210,7 +279,7 @@ def read(self, filename, encoding=None, binary=False):
210279
ifbinary:
211280
returncontent
212281
ifisinstance(content,bytes):
213-
returncontent.decode(encodingor'utf-8')
282+
returncontent.decode(encodingorget_default_encoding())
214283
returncontent
215284

216285
defreadlines(self,filename,num_lines=0,binary=False,encoding=None):

‎testgres/operations/os_ops.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
importlocale
2+
13
try:
24
importpsycopg2aspglib# noqa: F401
35
exceptImportError:
@@ -14,6 +16,10 @@ def __init__(self, host='127.0.0.1', ssh_key=None, username=None):
1416
self.username=username
1517

1618

19+
defget_default_encoding():
20+
returnlocale.getdefaultlocale()[1]or'UTF-8'
21+
22+
1723
classOsOperations:
1824
def__init__(self,username=None):
1925
self.ssh_key=None

‎testgres/operations/remote_ops.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
importlocale
21
importlogging
32
importos
43
importsubprocess
@@ -15,12 +14,7 @@
1514
raiseImportError("You must have psycopg2 or pg8000 modules installed")
1615

1716
from ..exceptionsimportExecUtilException
18-
19-
from .os_opsimportOsOperations,ConnectionParams
20-
21-
ConsoleEncoding=locale.getdefaultlocale()[1]
22-
ifnotConsoleEncoding:
23-
ConsoleEncoding='UTF-8'
17+
from .os_opsimportOsOperations,ConnectionParams,get_default_encoding
2418

2519
error_markers= [b'error',b'Permission denied',b'fatal',b'No such file or directory']
2620

@@ -283,7 +277,9 @@ def copytree(self, src, dst):
283277
returnself.exec_command("cp -r {} {}".format(src,dst))
284278

285279
# Work with files
286-
defwrite(self,filename,data,truncate=False,binary=False,read_and_write=False,encoding=ConsoleEncoding):
280+
defwrite(self,filename,data,truncate=False,binary=False,read_and_write=False,encoding=None):
281+
ifnotencoding:
282+
encoding=get_default_encoding()
287283
mode="wb"ifbinaryelse"w"
288284
ifnottruncate:
289285
mode="ab"ifbinaryelse"a"

‎testgres/utils.py

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,18 @@
33
from __future__importdivision
44
from __future__importprint_function
55

6+
importlocale
67
importos
7-
importport_for
8+
importrandom
9+
importsocket
10+
811
importsys
912

1013
fromcontextlibimportcontextmanager
1114
frompackaging.versionimportVersion,InvalidVersion
1215
importre
1316

17+
fromport_forimportPortForException
1418
fromsiximportiteritems
1519

1620
from .exceptionsimportExecUtilException
@@ -37,13 +41,49 @@ def reserve_port():
3741
"""
3842
Generate a new port and add it to 'bound_ports'.
3943
"""
40-
41-
port=port_for.select_random(exclude_ports=bound_ports)
44+
port=select_random(exclude_ports=bound_ports)
4245
bound_ports.add(port)
4346

4447
returnport
4548

4649

50+
defselect_random(
51+
ports=None,
52+
exclude_ports=None,
53+
)->int:
54+
"""
55+
Return random unused port number.
56+
Standard function from port_for does not work on Windows
57+
- an error 'port_for.exceptions.PortForException: Can't select a port'
58+
We should update it.
59+
"""
60+
ifportsisNone:
61+
ports=set(range(1024,65535))
62+
63+
ifexclude_portsisNone:
64+
exclude_ports=set()
65+
66+
ports.difference_update(set(exclude_ports))
67+
68+
sampled_ports=random.sample(tuple(ports),min(len(ports),100))
69+
70+
forportinsampled_ports:
71+
ifis_port_free(port):
72+
returnport
73+
74+
raisePortForException("Can't select a port")
75+
76+
77+
defis_port_free(port:int)->bool:
78+
"""Check if a port is free to use."""
79+
withsocket.socket(socket.AF_INET,socket.SOCK_STREAM)ass:
80+
try:
81+
s.bind(("",port))
82+
returnTrue
83+
exceptOSError:
84+
returnFalse
85+
86+
4787
defrelease_port(port):
4888
"""
4989
Free port provided by reserve_port().
@@ -80,7 +120,8 @@ def execute_utility(args, logfile=None, verbose=False):
80120
lines= [u'\n']+ ['# '+lineforlineinout.splitlines()]+ [u'\n']
81121
tconf.os_ops.write(filename=logfile,data=lines)
82122
exceptIOError:
83-
raiseExecUtilException("Problem with writing to logfile `{}` during run command `{}`".format(logfile,args))
123+
raiseExecUtilException(
124+
"Problem with writing to logfile `{}` during run command `{}`".format(logfile,args))
84125
ifverbose:
85126
returnexit_status,out,error
86127
else:

‎tests/test_simple.py

100755100644
Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,24 @@ def good_properties(f):
7474
returnTrue
7575

7676

77+
defrm_carriage_returns(out):
78+
"""
79+
In Windows we have additional '\r' symbols in output.
80+
Let's get rid of them.
81+
"""
82+
ifos.name=='nt':
83+
ifisinstance(out, (int,float,complex)):
84+
returnout
85+
elifisinstance(out,tuple):
86+
returntuple(rm_carriage_returns(item)foriteminout)
87+
elifisinstance(out,bytes):
88+
returnout.replace(b'\r',b'')
89+
else:
90+
returnout.replace('\r','')
91+
else:
92+
returnout
93+
94+
7795
@contextmanager
7896
defremoving(f):
7997
try:
@@ -254,34 +272,34 @@ def test_psql(self):
254272

255273
# check returned values (1 arg)
256274
res=node.psql('select 1')
257-
self.assertEqual(res, (0,b'1\n',b''))
275+
self.assertEqual(rm_carriage_returns(res), (0,b'1\n',b''))
258276

259277
# check returned values (2 args)
260278
res=node.psql('postgres','select 2')
261-
self.assertEqual(res, (0,b'2\n',b''))
279+
self.assertEqual(rm_carriage_returns(res), (0,b'2\n',b''))
262280

263281
# check returned values (named)
264282
res=node.psql(query='select 3',dbname='postgres')
265-
self.assertEqual(res, (0,b'3\n',b''))
283+
self.assertEqual(rm_carriage_returns(res), (0,b'3\n',b''))
266284

267285
# check returned values (1 arg)
268286
res=node.safe_psql('select 4')
269-
self.assertEqual(res,b'4\n')
287+
self.assertEqual(rm_carriage_returns(res),b'4\n')
270288

271289
# check returned values (2 args)
272290
res=node.safe_psql('postgres','select 5')
273-
self.assertEqual(res,b'5\n')
291+
self.assertEqual(rm_carriage_returns(res),b'5\n')
274292

275293
# check returned values (named)
276294
res=node.safe_psql(query='select 6',dbname='postgres')
277-
self.assertEqual(res,b'6\n')
295+
self.assertEqual(rm_carriage_returns(res),b'6\n')
278296

279297
# check feeding input
280298
node.safe_psql('create table horns (w int)')
281299
node.safe_psql('copy horns from stdin (format csv)',
282300
input=b"1\n2\n3\n\\.\n")
283301
_sum=node.safe_psql('select sum(w) from horns')
284-
self.assertEqual(_sum,b'6\n')
302+
self.assertEqual(rm_carriage_returns(_sum),b'6\n')
285303

286304
# check psql's default args, fails
287305
withself.assertRaises(QueryException):
@@ -455,7 +473,7 @@ def test_synchronous_replication(self):
455473
master.safe_psql(
456474
'insert into abc select generate_series(1, 1000000)')
457475
res=standby1.safe_psql('select count(*) from abc')
458-
self.assertEqual(res,b'1000000\n')
476+
self.assertEqual(rm_carriage_returns(res),b'1000000\n')
459477

460478
@unittest.skipUnless(pg_version_ge('10'),'requires 10+')
461479
deftest_logical_replication(self):
@@ -589,7 +607,7 @@ def test_promotion(self):
589607
# make standby becomes writable master
590608
replica.safe_psql('insert into abc values (1)')
591609
res=replica.safe_psql('select * from abc')
592-
self.assertEqual(res,b'1\n')
610+
self.assertEqual(rm_carriage_returns(res),b'1\n')
593611

594612
deftest_dump(self):
595613
query_create='create table test as select generate_series(1, 2) as val'
@@ -614,6 +632,7 @@ def test_users(self):
614632
withget_new_node().init().start()asnode:
615633
node.psql('create role test_user login')
616634
value=node.safe_psql('select 1',username='test_user')
635+
value=rm_carriage_returns(value)
617636
self.assertEqual(value,b'1\n')
618637

619638
deftest_poll_query_until(self):
@@ -977,7 +996,9 @@ def test_child_pids(self):
977996

978997
deftest_child_process_dies(self):
979998
# test for FileNotFound exception during child_processes() function
980-
withsubprocess.Popen(["sleep","60"])asprocess:
999+
cmd= ["timeout","60"]ifos.name=='nt'else ["sleep","60"]
1000+
1001+
withsubprocess.Popen(cmd,shell=True)asprocess:# shell=True might be needed on Windows
9811002
self.assertEqual(process.poll(),None)
9821003
# collect list of processes currently running
9831004
children=psutil.Process(os.getpid()).children()

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp