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

Commit1a2f6da

Browse files
authored
Fix initdb error on Windows (#99)
1 parent846c05f commit1a2f6da

12 files changed

+207
-122
lines changed

‎setup.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,16 @@
2727
readme=f.read()
2828

2929
setup(
30-
version='1.9.2',
30+
version='1.9.3',
3131
name='testgres',
32-
packages=['testgres','testgres.operations'],
32+
packages=['testgres','testgres.operations','testgres.helpers'],
3333
description='Testing utility for PostgreSQL and its extensions',
3434
url='https://github.com/postgrespro/testgres',
3535
long_description=readme,
3636
long_description_content_type='text/markdown',
3737
license='PostgreSQL',
38-
author='Ildar Musin',
39-
author_email='zildermann@gmail.com',
38+
author='Postgres Professional',
39+
author_email='testgres@postgrespro.ru',
4040
keywords=['test','testing','postgresql'],
4141
install_requires=install_requires,
4242
classifiers=[],

‎testgres/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
from .operations.local_opsimportLocalOperations
5353
from .operations.remote_opsimportRemoteOperations
5454

55+
from .helpers.port_managerimportPortManager
56+
5557
__all__= [
5658
"get_new_node",
5759
"get_remote_node",
@@ -62,6 +64,6 @@
6264
"XLogMethod","IsolationLevel","NodeStatus","ProcessType","DumpFormat",
6365
"PostgresNode","NodeApp",
6466
"reserve_port","release_port","bound_ports","get_bin_path","get_pg_config","get_pg_version",
65-
"First","Any",
67+
"First","Any","PortManager",
6668
"OsOperations","LocalOperations","RemoteOperations","ConnectionParams"
6769
]

‎testgres/helpers/__init__.py

Whitespace-only changes.

‎testgres/helpers/port_manager.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
importsocket
2+
importrandom
3+
fromtypingimportSet,Iterable,Optional
4+
5+
6+
classPortForException(Exception):
7+
pass
8+
9+
10+
classPortManager:
11+
def__init__(self,ports_range=(1024,65535)):
12+
self.ports_range=ports_range
13+
14+
@staticmethod
15+
defis_port_free(port:int)->bool:
16+
"""Check if a port is free to use."""
17+
withsocket.socket(socket.AF_INET,socket.SOCK_STREAM)ass:
18+
try:
19+
s.bind(("",port))
20+
returnTrue
21+
exceptOSError:
22+
returnFalse
23+
24+
deffind_free_port(self,ports:Optional[Set[int]]=None,exclude_ports:Optional[Iterable[int]]=None)->int:
25+
"""Return a random unused port number."""
26+
ifportsisNone:
27+
ports=set(range(1024,65535))
28+
29+
ifexclude_portsisNone:
30+
exclude_ports=set()
31+
32+
ports.difference_update(set(exclude_ports))
33+
34+
sampled_ports=random.sample(tuple(ports),min(len(ports),100))
35+
36+
forportinsampled_ports:
37+
ifself.is_port_free(port):
38+
returnport
39+
40+
raisePortForException("Can't select a port")

‎testgres/node.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -623,8 +623,8 @@ def status(self):
623623
"-D",self.data_dir,
624624
"status"
625625
]# yapf: disable
626-
status_code,out,err=execute_utility(_params,self.utils_log_file,verbose=True)
627-
if'does not exist'inerr:
626+
status_code,out,error=execute_utility(_params,self.utils_log_file,verbose=True)
627+
iferrorand'does not exist'inerror:
628628
returnNodeStatus.Uninitialized
629629
elif'no server running'inout:
630630
returnNodeStatus.Stopped
@@ -717,7 +717,7 @@ def start(self, params=[], wait=True):
717717

718718
try:
719719
exit_status,out,error=execute_utility(_params,self.utils_log_file,verbose=True)
720-
if'does not exist'inerror:
720+
iferrorand'does not exist'inerror:
721721
raiseException
722722
exceptExceptionase:
723723
msg='Cannot start node'
@@ -791,7 +791,7 @@ def restart(self, params=[]):
791791

792792
try:
793793
error_code,out,error=execute_utility(_params,self.utils_log_file,verbose=True)
794-
if'could not start server'inerror:
794+
iferrorand'could not start server'inerror:
795795
raiseExecUtilException
796796
exceptExecUtilExceptionase:
797797
msg='Cannot restart node'

‎testgres/operations/local_ops.py

Lines changed: 74 additions & 59 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,14 @@
2221
error_markers= [b'error',b'Permission denied',b'fatal']
2322

2423

24+
defhas_errors(output):
25+
ifoutput:
26+
ifisinstance(output,str):
27+
output=output.encode(get_default_encoding())
28+
returnany(markerinoutputformarkerinerror_markers)
29+
returnFalse
30+
31+
2532
classLocalOperations(OsOperations):
2633
def__init__(self,conn_params=None):
2734
ifconn_paramsisNone:
@@ -33,72 +40,80 @@ def __init__(self, conn_params=None):
3340
self.remote=False
3441
self.username=conn_params.usernameorself.get_user()
3542

36-
# Command execution
37-
defexec_command(self,cmd,wait_exit=False,verbose=False,
38-
expect_error=False,encoding=None,shell=False,text=False,
39-
input=None,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,
40-
get_process=None,timeout=None):
41-
"""
42-
Execute a command in a subprocess.
43-
44-
Args:
45-
- cmd: The command to execute.
46-
- wait_exit: Whether to wait for the subprocess to exit before returning.
47-
- verbose: Whether to return verbose output.
48-
- expect_error: Whether to raise an error if the subprocess exits with an error status.
49-
- encoding: The encoding to use for decoding the subprocess output.
50-
- shell: Whether to use shell when executing the subprocess.
51-
- text: Whether to return str instead of bytes for the subprocess output.
52-
- input: The input to pass to the subprocess.
53-
- stdout: The stdout to use for the subprocess.
54-
- stderr: The stderr to use for the subprocess.
55-
- proc: The process to use for subprocess creation.
56-
:return: The output of the subprocess.
57-
"""
58-
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
65-
else:
43+
@staticmethod
44+
def_raise_exec_exception(message,command,exit_code,output):
45+
"""Raise an ExecUtilException."""
46+
raiseExecUtilException(message=message.format(output),
47+
command=command,
48+
exit_code=exit_code,
49+
out=output)
50+
51+
@staticmethod
52+
def_process_output(encoding,temp_file_path):
53+
"""Process the output of a command from a temporary file."""
54+
withopen(temp_file_path,'rb')astemp_file:
55+
output=temp_file.read()
56+
ifencoding:
57+
output=output.decode(encoding)
58+
returnoutput,None# In Windows stderr writing in stdout
59+
60+
def_run_command(self,cmd,shell,input,stdin,stdout,stderr,get_process,timeout,encoding):
61+
"""Execute a command and return the process and its output."""
62+
ifos.name=='nt'andstdoutisNone:# Windows
63+
withtempfile.NamedTemporaryFile(mode='w+b',delete=False)astemp_file:
64+
stdout=temp_file
65+
stderr=subprocess.STDOUT
66+
process=subprocess.Popen(
67+
cmd,
68+
shell=shell,
69+
stdin=stdinorsubprocess.PIPEifinputisnotNoneelseNone,
70+
stdout=stdout,
71+
stderr=stderr,
72+
)
73+
ifget_process:
74+
returnprocess,None,None
75+
temp_file_path=temp_file.name
76+
77+
# Wait process finished
78+
process.wait()
79+
80+
output,error=self._process_output(encoding,temp_file_path)
81+
returnprocess,output,error
82+
else:# Other OS
6683
process=subprocess.Popen(
6784
cmd,
6885
shell=shell,
69-
stdout=stdout,
70-
stderr=stderr,
86+
stdin=stdinorsubprocess.PIPEifinputisnotNoneelseNone,
87+
stdout=stdoutorsubprocess.PIPE,
88+
stderr=stderrorsubprocess.PIPE,
7189
)
7290
ifget_process:
73-
returnprocess
74-
91+
returnprocess,None,None
7592
try:
76-
result,error=process.communicate(input,timeout=timeout)
93+
output,error=process.communicate(input=input.encode(encoding)ifinputelseNone,timeout=timeout)
94+
ifencoding:
95+
output=output.decode(encoding)
96+
error=error.decode(encoding)
97+
returnprocess,output,error
7798
exceptsubprocess.TimeoutExpired:
7899
process.kill()
79100
raiseExecUtilException("Command timed out after {} seconds.".format(timeout))
80-
exit_status=process.returncode
81-
82-
error_found=exit_status!=0orany(markerinerrorformarkerinerror_markers)
83101

84-
ifencoding:
85-
result=result.decode(encoding)
86-
error=error.decode(encoding)
87-
88-
ifexpect_error:
89-
raiseException(result,error)
90-
91-
ifexit_status!=0orerror_found:
92-
ifexit_status==0:
93-
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)
98-
ifverbose:
99-
returnexit_status,result,error
100-
else:
101-
returnresult
102+
defexec_command(self,cmd,wait_exit=False,verbose=False,expect_error=False,encoding=None,shell=False,
103+
text=False,input=None,stdin=None,stdout=None,stderr=None,get_process=False,timeout=None):
104+
"""
105+
Execute a command in a subprocess and handle the output based on the provided parameters.
106+
"""
107+
process,output,error=self._run_command(cmd,shell,input,stdin,stdout,stderr,get_process,timeout,encoding)
108+
ifget_process:
109+
returnprocess
110+
ifprocess.returncode!=0or (has_errors(error)andnotexpect_error):
111+
self._raise_exec_exception('Utility exited with non-zero code. Error `{}`',cmd,process.returncode,error)
112+
113+
ifverbose:
114+
returnprocess.returncode,output,error
115+
else:
116+
returnoutput
102117

103118
# Environment setup
104119
defenviron(self,var_name):
@@ -210,7 +225,7 @@ def read(self, filename, encoding=None, binary=False):
210225
ifbinary:
211226
returncontent
212227
ifisinstance(content,bytes):
213-
returncontent.decode(encodingor'utf-8')
228+
returncontent.decode(encodingorget_default_encoding())
214229
returncontent
215230

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

‎testgres/operations/os_ops.py

Lines changed: 7 additions & 1 deletion
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
@@ -75,7 +81,7 @@ def write(self, filename, data, truncate=False, binary=False, read_and_write=Fal
7581
deftouch(self,filename):
7682
raiseNotImplementedError()
7783

78-
defread(self,filename):
84+
defread(self,filename,encoding,binary):
7985
raiseNotImplementedError()
8086

8187
defreadlines(self,filename):

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp