Skip to content

Commit 3648587

Browse files
demonolockvshepard
authored andcommitted
Refactoring local_ops.py
1 parent de1ce5c commit 3648587

File tree

5 files changed

+63
-120
lines changed

5 files changed

+63
-120
lines changed

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
setup(
3030
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,

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_ops import LocalOperations
5353
from .operations.remote_ops import RemoteOperations
5454

55+
from .helpers.port_manager import PortManager
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/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' in err:
626+
status_code, out, error = execute_utility(_params, self.utils_log_file, verbose=True)
627+
if error and 'does not exist' in error:
628628
return NodeStatus.Uninitialized
629629
elif 'no server running' in out:
630630
return NodeStatus.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' in error:
720+
if error and 'does not exist' in error:
721721
raise Exception
722722
except Exception as e:
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' in error:
794+
if error and 'could not start server' in error:
795795
raise ExecUtilException
796796
except ExecUtilException as e:
797797
msg = 'Cannot restart node'

testgres/operations/local_ops.py

Lines changed: 54 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -38,32 +38,6 @@ def __init__(self, conn_params=None):
3838
self.remote = False
3939
self.username = conn_params.username or self.get_user()
4040

41-
@staticmethod
42-
def _run_command(cmd, shell, input, stdin, stdout, stderr, timeout, encoding, temp_file=None, get_process=None):
43-
"""Execute a command and return the process."""
44-
if temp_file is not None:
45-
stdout = stdout or temp_file
46-
stderr = stderr or subprocess.STDOUT
47-
else:
48-
stdout = stdout or subprocess.PIPE
49-
stderr = stderr or subprocess.PIPE
50-
51-
process = subprocess.Popen(
52-
cmd,
53-
shell=shell,
54-
stdin=stdin or subprocess.PIPE if input is not None else None,
55-
stdout=stdout,
56-
stderr=stderr,
57-
)
58-
59-
if get_process:
60-
return None, process
61-
try:
62-
return process.communicate(input=input.encode(encoding) if input else None, timeout=timeout), process
63-
except subprocess.TimeoutExpired:
64-
process.kill()
65-
raise ExecUtilException("Command timed out after {} seconds.".format(timeout))
66-
6741
@staticmethod
6842
def _raise_exec_exception(message, command, exit_code, output):
6943
"""Raise an ExecUtilException."""
@@ -72,105 +46,72 @@ def _raise_exec_exception(message, command, exit_code, output):
7246
exit_code=exit_code,
7347
out=output)
7448

75-
def exec_command(self, cmd, wait_exit=False, verbose=False,
76-
expect_error=False, encoding=None, shell=False, text=False,
77-
input=None, stdin=None, stdout=None, stderr=None,
78-
get_process=None, timeout=None):
79-
"""
80-
Execute a command in a subprocess.
81-
82-
Args:
83-
- cmd: The command to execute.
84-
- wait_exit: Whether to wait for the subprocess to exit before returning.
85-
- verbose: Whether to return verbose output.
86-
- expect_error: Whether to raise an error if the subprocess exits with an error status.
87-
- encoding: The encoding to use for decoding the subprocess output.
88-
- shell: Whether to use shell when executing the subprocess.
89-
- text: Whether to return str instead of bytes for the subprocess output.
90-
- input: The input to pass to the subprocess.
91-
- stdout: The stdout to use for the subprocess.
92-
- stderr: The stderr to use for the subprocess.
93-
- proc: The process to use for subprocess creation.
94-
:return: The output of the subprocess.
95-
"""
96-
if os.name == 'nt':
97-
return self._exec_command_windows(cmd, wait_exit=wait_exit, verbose=verbose,
98-
expect_error=expect_error, encoding=encoding, shell=shell, text=text,
99-
input=input, stdin=stdin, stdout=stdout, stderr=stderr,
100-
get_process=get_process, timeout=timeout)
101-
else:
49+
@staticmethod
50+
def _process_output(encoding, temp_file_path):
51+
"""Process the output of a command from a temporary file."""
52+
with open(temp_file_path, 'rb') as temp_file:
53+
output = temp_file.read()
54+
if encoding:
55+
output = output.decode(encoding)
56+
return output, None # In Windows stderr writing in stdout
57+
58+
def _run_command(self, cmd, shell, input, stdin, stdout, stderr, get_process, timeout, encoding):
59+
"""Execute a command and return the process and its output."""
60+
if os.name == 'nt' and stdout is None: # Windows
61+
with tempfile.NamedTemporaryFile(mode='w+b', delete=False) as temp_file:
62+
stdout = temp_file
63+
stderr = subprocess.STDOUT
64+
process = subprocess.Popen(
65+
cmd,
66+
shell=shell,
67+
stdin=stdin or subprocess.PIPE if input is not None else None,
68+
stdout=stdout,
69+
stderr=stderr,
70+
)
71+
if get_process:
72+
return process, None, None
73+
temp_file_path = temp_file.name
74+
75+
# Wait process finished
76+
process.wait()
77+
78+
output, error = self._process_output(encoding, temp_file_path)
79+
return process, output, error
80+
else: # Other OS
10281
process = subprocess.Popen(
10382
cmd,
10483
shell=shell,
105-
stdin=stdin,
106-
stdout=stdout,
107-
stderr=stderr,
84+
stdin=stdin or subprocess.PIPE if input is not None else None,
85+
stdout=stdout or subprocess.PIPE,
86+
stderr=stderr or subprocess.PIPE,
10887
)
10988
if get_process:
110-
return process
111-
89+
return process, None, None
11290
try:
113-
result, error = process.communicate(input, timeout=timeout)
91+
output, error = process.communicate(input=input.encode(encoding) if input else None, timeout=timeout)
92+
if encoding:
93+
output = output.decode(encoding)
94+
error = error.decode(encoding)
95+
return process, output, error
11496
except subprocess.TimeoutExpired:
11597
process.kill()
11698
raise ExecUtilException("Command timed out after {} seconds.".format(timeout))
117-
exit_status = process.returncode
11899

119-
error_found = exit_status != 0 or has_errors(error)
120-
121-
if encoding:
122-
result = result.decode(encoding)
123-
error = error.decode(encoding)
124-
125-
if expect_error:
126-
raise Exception(result, error)
127-
128-
if exit_status != 0 or error_found:
129-
if exit_status == 0:
130-
exit_status = 1
131-
self._raise_exec_exception('Utility exited with non-zero code. Error `{}`', cmd, exit_status, result)
132-
if verbose:
133-
return exit_status, result, error
134-
else:
135-
return result
100+
def exec_command(self, cmd, wait_exit=False, verbose=False, expect_error=False, encoding=None, shell=False,
101+
text=False, input=None, stdin=None, stdout=None, stderr=None, get_process=False, timeout=None):
102+
"""
103+
Execute a command in a subprocess and handle the output based on the provided parameters.
104+
"""
105+
process, output, error = self._run_command(cmd, shell, input, stdin, stdout, stderr, get_process, timeout, encoding)
106+
if get_process:
107+
return process
108+
if process.returncode != 0 or (has_errors(error) and not expect_error):
109+
self._raise_exec_exception('Utility exited with non-zero code. Error `{}`', cmd, process.returncode, error)
136110

137-
@staticmethod
138-
def _process_output(process, encoding, temp_file=None):
139-
"""Process the output of a command."""
140-
if temp_file is not None:
141-
temp_file.seek(0)
142-
output = temp_file.read()
111+
if verbose:
112+
return process.returncode, output, error
143113
else:
144-
output = process.stdout.read()
145-
146-
if encoding:
147-
output = output.decode(encoding)
148-
149-
return output
150-
151-
def _exec_command_windows(self, cmd, wait_exit=False, verbose=False,
152-
expect_error=False, encoding=None, shell=False, text=False,
153-
input=None, stdin=None, stdout=None, stderr=None,
154-
get_process=None, timeout=None):
155-
with tempfile.NamedTemporaryFile(mode='w+b') as temp_file:
156-
_, process = self._run_command(cmd, shell, input, stdin, stdout, stderr, timeout, encoding, temp_file, get_process)
157-
if get_process:
158-
return process
159-
result = self._process_output(process, encoding, temp_file)
160-
161-
if process.returncode != 0 or has_errors(result):
162-
if process.returncode == 0:
163-
process.returncode = 1
164-
if expect_error:
165-
if verbose:
166-
return process.returncode, result, result
167-
else:
168-
return result
169-
else:
170-
self._raise_exec_exception('Utility exited with non-zero code. Error `{}`', cmd, process.returncode,
171-
result)
172-
173-
return (process.returncode, result, result) if verbose else result
114+
return output
174115

175116
# Environment setup
176117
def environ(self, var_name):

testgres/operations/os_ops.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def write(self, filename, data, truncate=False, binary=False, read_and_write=Fal
8181
def touch(self, filename):
8282
raise NotImplementedError()
8383

84-
def read(self, filename):
84+
def read(self, filename, encoding, binary):
8585
raise NotImplementedError()
8686

8787
def readlines(self, filename):

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy