testgres 1.15.1__tar.gz → 1.16.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. {testgres-1.15.1/testgres.egg-info → testgres-1.16.0}/PKG-INFO +2 -2
  2. {testgres-1.15.1 → testgres-1.16.0}/pyproject.toml +1 -1
  3. {testgres-1.15.1 → testgres-1.16.0}/src/__init__.py +8 -3
  4. {testgres-1.15.1 → testgres-1.16.0}/src/backup.py +9 -2
  5. {testgres-1.15.1 → testgres-1.16.0}/src/cache.py +14 -7
  6. {testgres-1.15.1 → testgres-1.16.0}/src/exceptions.py +2 -0
  7. {testgres-1.15.1 → testgres-1.16.0}/src/impl/platforms/linux/internal_platform_utils.py +15 -18
  8. {testgres-1.15.1 → testgres-1.16.0}/src/logger.py +1 -1
  9. {testgres-1.15.1 → testgres-1.16.0}/src/node.py +130 -40
  10. {testgres-1.15.1 → testgres-1.16.0}/src/utils.py +92 -40
  11. {testgres-1.15.1 → testgres-1.16.0/testgres.egg-info}/PKG-INFO +2 -2
  12. {testgres-1.15.1 → testgres-1.16.0}/testgres.egg-info/requires.txt +1 -1
  13. {testgres-1.15.1 → testgres-1.16.0}/tests/test_os_ops_common.py +2697 -30
  14. {testgres-1.15.1 → testgres-1.16.0}/tests/test_os_ops_local.py +3 -3
  15. {testgres-1.15.1 → testgres-1.16.0}/tests/test_os_ops_remote.py +28 -5
  16. {testgres-1.15.1 → testgres-1.16.0}/tests/test_testgres_common.py +2 -2
  17. {testgres-1.15.1 → testgres-1.16.0}/tests/test_utils.py +3 -3
  18. {testgres-1.15.1 → testgres-1.16.0}/LICENSE +0 -0
  19. {testgres-1.15.1 → testgres-1.16.0}/README.md +0 -0
  20. {testgres-1.15.1 → testgres-1.16.0}/setup.cfg +0 -0
  21. {testgres-1.15.1 → testgres-1.16.0}/src/api.py +0 -0
  22. {testgres-1.15.1 → testgres-1.16.0}/src/config.py +0 -0
  23. {testgres-1.15.1 → testgres-1.16.0}/src/connection.py +0 -0
  24. {testgres-1.15.1 → testgres-1.16.0}/src/consts.py +0 -0
  25. {testgres-1.15.1 → testgres-1.16.0}/src/decorators.py +0 -0
  26. {testgres-1.15.1 → testgres-1.16.0}/src/defaults.py +0 -0
  27. {testgres-1.15.1 → testgres-1.16.0}/src/enums.py +0 -0
  28. {testgres-1.15.1 → testgres-1.16.0}/src/impl/file_line_reader.py +0 -0
  29. {testgres-1.15.1 → testgres-1.16.0}/src/impl/internal_utils.py +0 -0
  30. {testgres-1.15.1 → testgres-1.16.0}/src/impl/platforms/internal_platform_utils.py +0 -0
  31. {testgres-1.15.1 → testgres-1.16.0}/src/impl/platforms/internal_platform_utils_factory.py +0 -0
  32. {testgres-1.15.1 → testgres-1.16.0}/src/impl/platforms/win32/internal_platform_utils.py +0 -0
  33. {testgres-1.15.1 → testgres-1.16.0}/src/impl/port_manager__generic.py +0 -0
  34. {testgres-1.15.1 → testgres-1.16.0}/src/impl/port_manager__generic2.py +0 -0
  35. {testgres-1.15.1 → testgres-1.16.0}/src/impl/port_manager__this_host.py +0 -0
  36. {testgres-1.15.1 → testgres-1.16.0}/src/node_app.py +0 -0
  37. {testgres-1.15.1 → testgres-1.16.0}/src/port_manager.py +0 -0
  38. {testgres-1.15.1 → testgres-1.16.0}/src/pubsub.py +0 -0
  39. {testgres-1.15.1 → testgres-1.16.0}/src/raise_error.py +0 -0
  40. {testgres-1.15.1 → testgres-1.16.0}/src/standby.py +0 -0
  41. {testgres-1.15.1 → testgres-1.16.0}/testgres.egg-info/SOURCES.txt +0 -0
  42. {testgres-1.15.1 → testgres-1.16.0}/testgres.egg-info/dependency_links.txt +0 -0
  43. {testgres-1.15.1 → testgres-1.16.0}/testgres.egg-info/top_level.txt +0 -0
  44. {testgres-1.15.1 → testgres-1.16.0}/tests/test_api.py +0 -0
  45. {testgres-1.15.1 → testgres-1.16.0}/tests/test_config.py +0 -0
  46. {testgres-1.15.1 → testgres-1.16.0}/tests/test_raise_error.py +0 -0
  47. {testgres-1.15.1 → testgres-1.16.0}/tests/test_testgres_local.py +0 -0
  48. {testgres-1.15.1 → testgres-1.16.0}/tests/test_testgres_remote.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: testgres
3
- Version: 1.15.1
3
+ Version: 1.16.0
4
4
  Summary: Testing utility for PostgreSQL and its extensions
5
5
  Author-email: Postgres Professional <testgres@postgrespro.ru>
6
6
  License: PostgreSQL
@@ -27,7 +27,7 @@ Requires-Dist: port-for>=0.4
27
27
  Requires-Dist: six>=1.9.0
28
28
  Requires-Dist: psutil
29
29
  Requires-Dist: packaging
30
- Requires-Dist: testgres.os_ops<4.0.0,>=3.2.0
30
+ Requires-Dist: testgres.os_ops<4.0.0,>=3.3.0
31
31
  Dynamic: license-file
32
32
 
33
33
  [![CI Status](https://img.shields.io/github/actions/workflow/status/postgrespro/testgres/.github/workflows/ci.yml?label=CI)](https://github.com/postgrespro/testgres/actions/workflows/ci.yml)
@@ -65,7 +65,7 @@ dependencies = [
65
65
  "six>=1.9.0",
66
66
  "psutil",
67
67
  "packaging",
68
- "testgres.os_ops>=3.2.0,<4.0.0",
68
+ "testgres.os_ops>=3.3.0,<4.0.0",
69
69
  ]
70
70
 
71
71
  [project.urls]
@@ -18,6 +18,7 @@ from .connection import \
18
18
  from .exceptions import \
19
19
  TestgresException, \
20
20
  ExecUtilException, \
21
+ ExecTimeoutException, \
21
22
  QueryException, \
22
23
  QueryTimeoutException, \
23
24
  TimeoutException, \
@@ -53,11 +54,13 @@ from .standby import \
53
54
 
54
55
  from .config import testgres_config
55
56
 
56
- from testgres.operations.os_ops import OsOperations, ConnectionParams
57
+ from testgres.operations.os_ops import OsOperations
58
+ from testgres.operations.os_ops import ConnectionParams
59
+ from testgres.operations.os_ops import OsProcessController
57
60
  from testgres.operations.local_ops import LocalOperations
58
61
  from testgres.operations.remote_ops import RemoteOperations
59
62
 
60
- __version__ = "1.15.1"
63
+ __version__ = "1.16.0"
61
64
 
62
65
  __all__ = [
63
66
  "get_new_node",
@@ -66,6 +69,7 @@ __all__ = [
66
69
  "TestgresConfig", "configure_testgres", "scoped_config", "push_config", "pop_config",
67
70
  "NodeConnection", "DatabaseError", "InternalError", "ProgrammingError", "OperationalError",
68
71
  "TestgresException", "ExecUtilException", "QueryException",
72
+ "ExecTimeoutException",
69
73
  "QueryTimeoutException",
70
74
  "TimeoutException", "CatchUpException", "StartNodeException", "InitNodeException", "BackupException", "InvalidOperationException",
71
75
  "XLogMethod", "IsolationLevel", "NodeStatus", "ProcessType", "DumpFormat",
@@ -74,5 +78,6 @@ __all__ = [
74
78
  "PortManager",
75
79
  "reserve_port", "release_port", "get_bin_path", "get_bin_dir", "get_pg_config", "get_pg_version", "parse_pg_version",
76
80
  "First", "Any",
77
- "OsOperations", "LocalOperations", "RemoteOperations", "ConnectionParams"
81
+ "OsOperations", "LocalOperations", "RemoteOperations", "ConnectionParams",
82
+ "OsProcessController",
78
83
  ]
@@ -15,9 +15,10 @@ from .exceptions import BackupException
15
15
 
16
16
  from testgres.operations.os_ops import OsOperations
17
17
 
18
+ from . import utils
19
+
18
20
  from .utils import \
19
21
  get_bin_path2, \
20
- execute_utility2, \
21
22
  clean_on_error
22
23
 
23
24
 
@@ -86,7 +87,13 @@ class NodeBackup(object):
86
87
  "-X", xlog_method.value
87
88
  ] # yapf: disable
88
89
  _params += options
89
- execute_utility2(self.os_ops, _params, self.log_file)
90
+
91
+ utils.execute_utility3(
92
+ self.os_ops,
93
+ _params,
94
+ self.log_file,
95
+ )
96
+ return
90
97
 
91
98
  def __enter__(self):
92
99
  return self
@@ -8,14 +8,12 @@ from .consts import XLOG_CONTROL_FILE
8
8
 
9
9
  from .defaults import generate_system_id
10
10
 
11
+ from . import utils
12
+
11
13
  from .exceptions import \
12
14
  InitNodeException, \
13
15
  ExecUtilException
14
16
 
15
- from .utils import \
16
- get_bin_path2, \
17
- execute_utility2
18
-
19
17
  from testgres.operations.local_ops import LocalOperations
20
18
  from testgres.operations.os_ops import OsOperations
21
19
 
@@ -39,13 +37,17 @@ def cached_initdb(data_dir, logfile=None, params=None, os_ops: OsOperations = No
39
37
  if bin_path:
40
38
  return os_ops.build_path(bin_path, name)
41
39
 
42
- return get_bin_path2(os_ops, name)
40
+ return utils.get_bin_path2(os_ops, name)
43
41
 
44
42
  def call_initdb(initdb_dir, log=logfile):
45
43
  try:
46
44
  initdb_path = make_utility_path("initdb")
47
45
  _params = [initdb_path, "-D", initdb_dir, "-N"]
48
- execute_utility2(os_ops, _params + (params or []), log)
46
+ utils.execute_utility3(
47
+ os_ops,
48
+ _params + (params or []),
49
+ log,
50
+ )
49
51
  except ExecUtilException as e:
50
52
  raise_from(InitNodeException("Failed to run initdb"), e)
51
53
 
@@ -78,7 +80,12 @@ def cached_initdb(data_dir, logfile=None, params=None, os_ops: OsOperations = No
78
80
 
79
81
  # XXX: build new WAL segment with our system id
80
82
  _params = [make_utility_path("pg_resetwal"), "-D", data_dir, "-f"]
81
- execute_utility2(os_ops, _params, logfile)
83
+
84
+ utils.execute_utility3(
85
+ os_ops,
86
+ _params,
87
+ logfile,
88
+ )
82
89
 
83
90
  except ExecUtilException as e:
84
91
  msg = "Failed to reset WAL for system id"
@@ -6,6 +6,7 @@ import typing
6
6
  from testgres.operations.exceptions import TestgresException
7
7
  from testgres.operations.exceptions import ExecUtilException
8
8
  from testgres.operations.exceptions import InvalidOperationException
9
+ from testgres.operations.exceptions import ExecTimeoutException
9
10
 
10
11
 
11
12
  class PortForException(TestgresException):
@@ -298,3 +299,4 @@ class BackupException(TestgresException):
298
299
 
299
300
  assert ExecUtilException.__name__ == "ExecUtilException"
300
301
  assert InvalidOperationException.__name__ == "InvalidOperationException"
302
+ assert ExecTimeoutException.__name__ == "ExecTimeoutException"
@@ -5,7 +5,9 @@ from ... import internal_utils
5
5
  from ....raise_error import RaiseError
6
6
 
7
7
  from testgres.operations.os_ops import OsOperations
8
+ from testgres.operations.os_ops import OsCommandResult
8
9
  from testgres.operations.exceptions import ExecUtilException
10
+ from testgres.operations.types import T_OS_EXEC_ENV
9
11
 
10
12
  import re
11
13
  import shlex
@@ -17,7 +19,7 @@ class InternalPlatformUtils(base.InternalPlatformUtils):
17
19
  C_MAX_FIND_POSTMASTER_ATTEMPTS = 5
18
20
  C_BASH_EXE = "/bin/bash"
19
21
 
20
- sm_exec_env = {
22
+ sm_exec_env: T_OS_EXEC_ENV = {
21
23
  "LANG": "en_US.UTF-8",
22
24
  "LC_ALL": "en_US.UTF-8",
23
25
  }
@@ -113,37 +115,32 @@ class InternalPlatformUtils(base.InternalPlatformUtils):
113
115
  "ps -ewwo \"pid=,ppid=,args=\" | grep -E " + shlex.quote(regexp),
114
116
  ]
115
117
 
116
- exec_r = os_ops.exec_command(
118
+ exec_r = os_ops.run(
117
119
  cmd=cmd,
118
- ignore_errors=True,
119
- verbose=True,
120
+ check=False,
120
121
  exec_env=__class__.sm_exec_env,
121
122
  )
122
123
 
123
- assert type(exec_r) is tuple
124
- assert len(exec_r) == 3
124
+ assert type(exec_r) is OsCommandResult
125
+ assert type(exec_r.returncode) is int
126
+ assert type(exec_r.stdout) is bytes
127
+ assert type(exec_r.stderr) is bytes
125
128
 
126
- exit_status, output_b, error_b = exec_r
127
-
128
- assert type(exit_status) is int
129
- assert type(output_b) is bytes
130
- assert type(error_b) is bytes
131
-
132
- if exit_status == 1:
129
+ if exec_r.returncode == 1:
133
130
  return None
134
131
 
135
- output = output_b.decode("utf-8")
136
- error = error_b.decode("utf-8")
132
+ output = exec_r.stdout.decode("utf-8")
133
+ error = exec_r.stderr.decode("utf-8")
137
134
 
138
135
  assert type(output) is str
139
136
  assert type(error) is str
140
137
 
141
- if exit_status != 0:
142
- errMsg = f"test command returned an unexpected exit code: {exit_status}"
138
+ if exec_r.returncode != 0:
139
+ errMsg = f"test command returned an unexpected exit code: {exec_r.returncode}"
143
140
  raise ExecUtilException(
144
141
  message=errMsg,
145
142
  command=cmd,
146
- exit_code=exit_status,
143
+ exit_code=exec_r.returncode,
147
144
  out=output,
148
145
  error=error,
149
146
  )
@@ -31,7 +31,7 @@ class TestgresLogger(threading.Thread):
31
31
  assert type(log_file_encoding) is str
32
32
  assert os_ops is None or isinstance(os_ops, OsOperations)
33
33
 
34
- threading.Thread.__init__(self)
34
+ threading.Thread.__init__(self, daemon=True)
35
35
 
36
36
  if os_ops is None:
37
37
  os_ops = tconf.os_ops
@@ -92,7 +92,6 @@ from .utils import \
92
92
  PgVer, \
93
93
  eprint, \
94
94
  get_pg_version2, \
95
- execute_utility2, \
96
95
  options_string, \
97
96
  clean_on_error
98
97
 
@@ -101,6 +100,8 @@ from .raise_error import RaiseError
101
100
  from .backup import NodeBackup
102
101
 
103
102
  from testgres.operations.os_ops import OsOperations
103
+ from testgres.operations.os_ops import OsCommandResult
104
+ from testgres.operations.os_ops import OsProcessController
104
105
  from testgres.operations.local_ops import LocalOperations
105
106
 
106
107
  InternalError = pglib.InternalError
@@ -638,6 +639,12 @@ class PostgresNode(object):
638
639
  assert type(with_force) is bool
639
640
  assert max_attempts > 0
640
641
 
642
+ try:
643
+ self._try_shutdown_internal(max_attempts, with_force)
644
+ finally:
645
+ self._maybe_stop_logger()
646
+
647
+ def _try_shutdown_internal(self, max_attempts, with_force):
641
648
  attempts = 0
642
649
 
643
650
  # try stopping server N times
@@ -668,7 +675,11 @@ class PostgresNode(object):
668
675
 
669
676
  ps_command = ['ps', '-o', 'pid=', '-p', str(node_pid)]
670
677
 
671
- ps_output = self._os_ops.exec_command(cmd=ps_command, shell=True, ignore_errors=True).decode('utf-8')
678
+ ps_output = self._os_ops.run(
679
+ cmd=ps_command,
680
+ shell=True,
681
+ check=False,
682
+ ).stdout.decode('utf-8')
672
683
  assert type(ps_output) is str
673
684
 
674
685
  if ps_output == "":
@@ -687,7 +698,11 @@ class PostgresNode(object):
687
698
  pass
688
699
 
689
700
  # Check that node stopped - print only column pid without headers
690
- ps_output = self._os_ops.exec_command(cmd=ps_command, shell=True, ignore_errors=True).decode('utf-8')
701
+ ps_output = self._os_ops.run(
702
+ cmd=ps_command,
703
+ shell=True,
704
+ check=False,
705
+ ).stdout.decode('utf-8')
691
706
  assert type(ps_output) is str
692
707
 
693
708
  if ps_output == "":
@@ -1087,11 +1102,16 @@ class PostgresNode(object):
1087
1102
  _params += ["-D"] if self._pg_version >= PgVer('9.5') else []
1088
1103
  _params += [self.data_dir]
1089
1104
 
1090
- data = execute_utility2(self._os_ops, _params, self.utils_log_file)
1105
+ exec_r = utils.execute_utility3(
1106
+ self._os_ops,
1107
+ _params,
1108
+ self.utils_log_file,
1109
+ )
1110
+ assert type(exec_r.stdout) is str
1091
1111
 
1092
1112
  out_dict = {}
1093
1113
 
1094
- for line in data.splitlines():
1114
+ for line in exec_r.stdout.splitlines():
1095
1115
  key, _, value = line.partition(':')
1096
1116
  out_dict[key.strip()] = value.strip()
1097
1117
 
@@ -1242,10 +1262,17 @@ class PostgresNode(object):
1242
1262
 
1243
1263
  def LOCAL__start_node():
1244
1264
  # 'error' will be None on Windows
1245
- _, _, error = execute_utility2(self._os_ops, _params, self.utils_log_file, verbose=True, exec_env=exec_env)
1246
- assert error is None or type(error) is str
1247
- if error and 'does not exist' in error:
1248
- raise Exception(error)
1265
+ exec_r = utils.execute_utility3(
1266
+ self._os_ops,
1267
+ _params,
1268
+ self.utils_log_file,
1269
+ exec_env=exec_env,
1270
+ )
1271
+ # TODO: WTF? Remove it!
1272
+ assert exec_r.returncode == 0
1273
+ assert exec_r.stderr is None or type(exec_r.stderr) is str
1274
+ if exec_r.stderr is not None and 'does not exist' in exec_r.stderr:
1275
+ raise ExecUtilException(exec_r.stderr)
1249
1276
 
1250
1277
  def LOCAL__raise_cannot_start_node__std(from_exception):
1251
1278
  assert isinstance(from_exception, Exception)
@@ -1331,11 +1358,17 @@ class PostgresNode(object):
1331
1358
  "stop"
1332
1359
  ] + params # yapf: disable
1333
1360
 
1334
- execute_utility2(self._os_ops, _params, self.utils_log_file)
1335
-
1336
- self._manually_started_pm_pid = None
1337
-
1338
- self._maybe_stop_logger()
1361
+ try:
1362
+ utils.execute_utility3(
1363
+ self._os_ops,
1364
+ _params,
1365
+ logfile=self.utils_log_file,
1366
+ )
1367
+ self._manually_started_pm_pid = None
1368
+ finally:
1369
+ # always stop the reader thread, even if pg_ctl stop failed,
1370
+ # so it can't leak into interpreter shutdown.
1371
+ self._maybe_stop_logger()
1339
1372
  return self
1340
1373
 
1341
1374
  def kill(self, someone=None):
@@ -1391,9 +1424,16 @@ class PostgresNode(object):
1391
1424
  ] + params # yapf: disable
1392
1425
 
1393
1426
  try:
1394
- error_code, out, error = execute_utility2(self._os_ops, _params, self.utils_log_file, verbose=True)
1395
- if error and 'could not start server' in error:
1396
- raise ExecUtilException
1427
+ exec_r = utils.execute_utility3(
1428
+ self._os_ops,
1429
+ _params,
1430
+ logfile=self.utils_log_file,
1431
+ )
1432
+ # TODO: WTF? Remove it!
1433
+ assert exec_r.returncode == 0
1434
+ assert exec_r.stderr is None or type(exec_r.stderr) is str
1435
+ if exec_r.stderr is not None and 'could not start server' in exec_r.stderr:
1436
+ raise ExecUtilException(exec_r.stderr)
1397
1437
  except ExecUtilException as e:
1398
1438
  msg = 'Cannot restart node'
1399
1439
  files = self._collect_special_files()
@@ -1420,7 +1460,11 @@ class PostgresNode(object):
1420
1460
  "reload"
1421
1461
  ] + params # yapf: disable
1422
1462
 
1423
- execute_utility2(self._os_ops, _params, self.utils_log_file)
1463
+ utils.execute_utility3(
1464
+ self._os_ops,
1465
+ _params,
1466
+ logfile=self.utils_log_file,
1467
+ )
1424
1468
 
1425
1469
  return self
1426
1470
 
@@ -1442,7 +1486,11 @@ class PostgresNode(object):
1442
1486
  "promote"
1443
1487
  ] # yapf: disable
1444
1488
 
1445
- execute_utility2(self._os_ops, _params, self.utils_log_file)
1489
+ utils.execute_utility3(
1490
+ self._os_ops,
1491
+ _params,
1492
+ self.utils_log_file,
1493
+ )
1446
1494
 
1447
1495
  # for versions below 10 `promote` is asynchronous so we need to wait
1448
1496
  # until it actually becomes writable
@@ -1477,7 +1525,11 @@ class PostgresNode(object):
1477
1525
  "-w" # wait
1478
1526
  ] + params # yapf: disable
1479
1527
 
1480
- return execute_utility2(self._os_ops, _params, self.utils_log_file)
1528
+ return utils.execute_utility3(
1529
+ self._os_ops,
1530
+ _params,
1531
+ self.utils_log_file,
1532
+ ).stdout
1481
1533
 
1482
1534
  def release_resources(self):
1483
1535
  """
@@ -1556,7 +1608,7 @@ class PostgresNode(object):
1556
1608
  assert port is None or type(port) is int
1557
1609
  assert type(variables) is dict
1558
1610
 
1559
- return self._psql(
1611
+ r = self._psql(
1560
1612
  ignore_errors=True,
1561
1613
  query=query,
1562
1614
  filename=filename,
@@ -1567,6 +1619,8 @@ class PostgresNode(object):
1567
1619
  port=port,
1568
1620
  **variables
1569
1621
  )
1622
+ assert type(r) is OsCommandResult
1623
+ return r.returncode, r.stdout, r.stderr
1570
1624
 
1571
1625
  def _psql(
1572
1626
  self,
@@ -1578,7 +1632,8 @@ class PostgresNode(object):
1578
1632
  input=None,
1579
1633
  host: typing.Optional[str] = None,
1580
1634
  port: typing.Optional[int] = None,
1581
- **variables):
1635
+ **variables
1636
+ ) -> OsCommandResult:
1582
1637
  assert host is None or type(host) is str
1583
1638
  assert port is None or type(port) is int
1584
1639
  assert type(variables) is dict
@@ -1628,13 +1683,15 @@ class PostgresNode(object):
1628
1683
  else:
1629
1684
  raise QueryException('Query or filename must be provided')
1630
1685
 
1631
- return self._os_ops.exec_command(
1686
+ r = self._os_ops.run(
1632
1687
  psql_params,
1633
- verbose=True,
1634
1688
  input=input,
1635
1689
  stderr=subprocess.PIPE,
1636
1690
  stdout=subprocess.PIPE,
1637
- ignore_errors=ignore_errors)
1691
+ check=not ignore_errors,
1692
+ )
1693
+ assert type(r) is OsCommandResult
1694
+ return r
1638
1695
 
1639
1696
  @method_decorator(positional_args_hack(['dbname', 'query']))
1640
1697
  def safe_psql(self, query=None, expect_error=False, **kwargs):
@@ -1662,7 +1719,7 @@ class PostgresNode(object):
1662
1719
  # force this setting
1663
1720
  kwargs['ON_ERROR_STOP'] = 1
1664
1721
  try:
1665
- ret, out, err = self._psql(ignore_errors=False, query=query, **kwargs)
1722
+ exec_r = self._psql(ignore_errors=False, query=query, **kwargs)
1666
1723
  except ExecUtilException as e:
1667
1724
  if not expect_error:
1668
1725
  raise QueryException(e.message, query)
@@ -1677,7 +1734,7 @@ class PostgresNode(object):
1677
1734
  if expect_error:
1678
1735
  raise InvalidOperationException("Exception was expected, but query finished successfully: `{}`.".format(query))
1679
1736
 
1680
- return out
1737
+ return exec_r.stdout
1681
1738
 
1682
1739
  def dump(self,
1683
1740
  filename=None,
@@ -1732,7 +1789,11 @@ class PostgresNode(object):
1732
1789
  if options:
1733
1790
  _params.extend(options)
1734
1791
 
1735
- execute_utility2(self._os_ops, _params, self.utils_log_file)
1792
+ utils.execute_utility3(
1793
+ self._os_ops,
1794
+ _params,
1795
+ self.utils_log_file,
1796
+ )
1736
1797
 
1737
1798
  return filename
1738
1799
 
@@ -1761,7 +1822,11 @@ class PostgresNode(object):
1761
1822
 
1762
1823
  # try pg_restore if dump is binary format, and psql if not
1763
1824
  try:
1764
- execute_utility2(self._os_ops, _params, self.utils_log_file)
1825
+ utils.execute_utility3(
1826
+ self._os_ops,
1827
+ _params,
1828
+ self.utils_log_file,
1829
+ )
1765
1830
  except ExecUtilException:
1766
1831
  self.psql(filename=filename, dbname=dbname, username=username)
1767
1832
 
@@ -2002,12 +2067,14 @@ class PostgresNode(object):
2002
2067
  dbname=dbname, username=username, **params)
2003
2068
  # yapf: enable
2004
2069
 
2005
- def pgbench(self,
2006
- dbname=None,
2007
- username=None,
2008
- stdout=None,
2009
- stderr=None,
2010
- options=None):
2070
+ def pgbench(
2071
+ self,
2072
+ dbname=None,
2073
+ username=None,
2074
+ stdout=None,
2075
+ stderr=None,
2076
+ options=None,
2077
+ ) -> OsProcessController:
2011
2078
  """
2012
2079
  Spawn a pgbench process.
2013
2080
 
@@ -2019,7 +2086,7 @@ class PostgresNode(object):
2019
2086
  options: additional options for pgbench (list).
2020
2087
 
2021
2088
  Returns:
2022
- Process created by subprocess.Popen.
2089
+ OsProcessController.
2023
2090
  """
2024
2091
  if options is None:
2025
2092
  options = []
@@ -2036,10 +2103,14 @@ class PostgresNode(object):
2036
2103
  # should be the last one
2037
2104
  _params.append(dbname)
2038
2105
 
2039
- proc = self._os_ops.exec_command(_params, stdout=stdout, stderr=stderr, get_process=True)
2106
+ proc = self._os_ops.popen(
2107
+ _params,
2108
+ stdout=stdout,
2109
+ stderr=stderr,
2110
+ )
2040
2111
 
2041
2112
  # [2026-06-21] It is so
2042
- assert isinstance(proc, subprocess.Popen)
2113
+ assert isinstance(proc, OsProcessController)
2043
2114
  return proc
2044
2115
 
2045
2116
  def pgbench_with_wait(self,
@@ -2122,7 +2193,11 @@ class PostgresNode(object):
2122
2193
  # should be the last one
2123
2194
  _params.append(dbname)
2124
2195
 
2125
- return execute_utility2(self._os_ops, _params, self.utils_log_file)
2196
+ return utils.execute_utility3(
2197
+ self._os_ops,
2198
+ _params,
2199
+ self.utils_log_file,
2200
+ ).stdout
2126
2201
 
2127
2202
  def connect(self,
2128
2203
  dbname=None,
@@ -2295,7 +2370,22 @@ class PostgresNode(object):
2295
2370
  ]
2296
2371
  upgrade_command += options
2297
2372
 
2298
- return self._os_ops.exec_command(upgrade_command, expect_error=expect_error)
2373
+ r: typing.Optional[typing.Any] = None
2374
+ try:
2375
+ r = self._os_ops.run(upgrade_command).stdout
2376
+ except BaseException as e:
2377
+ if not expect_error:
2378
+ raise
2379
+
2380
+ logging.info("Exception ({}): {}".format(
2381
+ type(e).__name__,
2382
+ e,
2383
+ ))
2384
+
2385
+ if expect_error:
2386
+ raise RuntimeError("Operation executed without any errors.")
2387
+
2388
+ return r
2299
2389
 
2300
2390
  def _release_resources(self):
2301
2391
  self._free_port()