testgres 1.13.7__tar.gz → 1.14.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.
- {testgres-1.13.7/testgres.egg-info → testgres-1.14.0}/PKG-INFO +3 -4
- {testgres-1.13.7 → testgres-1.14.0}/README.md +1 -2
- {testgres-1.13.7 → testgres-1.14.0}/pyproject.toml +1 -1
- {testgres-1.13.7 → testgres-1.14.0}/src/__init__.py +1 -1
- {testgres-1.13.7 → testgres-1.14.0}/src/api.py +11 -2
- {testgres-1.13.7 → testgres-1.14.0}/src/backup.py +1 -6
- {testgres-1.13.7 → testgres-1.14.0}/src/node.py +84 -73
- {testgres-1.13.7 → testgres-1.14.0}/src/node_app.py +40 -40
- {testgres-1.13.7 → testgres-1.14.0}/src/utils.py +28 -1
- {testgres-1.13.7 → testgres-1.14.0/testgres.egg-info}/PKG-INFO +3 -4
- {testgres-1.13.7 → testgres-1.14.0}/testgres.egg-info/SOURCES.txt +1 -0
- {testgres-1.13.7 → testgres-1.14.0}/testgres.egg-info/requires.txt +1 -1
- testgres-1.14.0/tests/test_api.py +30 -0
- {testgres-1.13.7 → testgres-1.14.0}/tests/test_testgres_common.py +6 -4
- {testgres-1.13.7 → testgres-1.14.0}/LICENSE +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/setup.cfg +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/cache.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/config.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/connection.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/consts.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/decorators.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/defaults.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/enums.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/exceptions.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/impl/internal_utils.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/impl/platforms/internal_platform_utils.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/impl/platforms/internal_platform_utils_factory.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/impl/platforms/linux/internal_platform_utils.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/impl/platforms/win32/internal_platform_utils.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/impl/port_manager__generic.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/impl/port_manager__this_host.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/logger.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/port_manager.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/pubsub.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/raise_error.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/src/standby.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/testgres.egg-info/dependency_links.txt +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/testgres.egg-info/top_level.txt +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/tests/test_config.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/tests/test_os_ops_common.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/tests/test_os_ops_local.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/tests/test_os_ops_remote.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/tests/test_raise_error.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/tests/test_testgres_local.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/tests/test_testgres_remote.py +0 -0
- {testgres-1.13.7 → testgres-1.14.0}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: testgres
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.14.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,11 +27,10 @@ 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<3.0.0,>=2.
|
|
30
|
+
Requires-Dist: testgres.os_ops<3.0.0,>=2.3.0
|
|
31
31
|
Dynamic: license-file
|
|
32
32
|
|
|
33
|
-
[](https://codecov.io/gh/postgrespro/testgres)
|
|
33
|
+
[](https://github.com/postgrespro/testgres/actions/workflows/ci.yml)
|
|
35
34
|
[](https://badge.fury.io/py/testgres)
|
|
36
35
|
[](https://pypi.org/project/testgres)
|
|
37
36
|
[](https://pypi.org/project/testgres)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
[](https://codecov.io/gh/postgrespro/testgres)
|
|
1
|
+
[](https://github.com/postgrespro/testgres/actions/workflows/ci.yml)
|
|
3
2
|
[](https://badge.fury.io/py/testgres)
|
|
4
3
|
[](https://pypi.org/project/testgres)
|
|
5
4
|
[](https://pypi.org/project/testgres)
|
|
@@ -56,7 +56,7 @@ from testgres.operations.os_ops import OsOperations, ConnectionParams
|
|
|
56
56
|
from testgres.operations.local_ops import LocalOperations
|
|
57
57
|
from testgres.operations.remote_ops import RemoteOperations
|
|
58
58
|
|
|
59
|
-
__version__ = "1.
|
|
59
|
+
__version__ = "1.14.0"
|
|
60
60
|
|
|
61
61
|
__all__ = [
|
|
62
62
|
"get_new_node",
|
|
@@ -31,6 +31,10 @@ PostgresNode(name='...', port=..., base_dir='...')
|
|
|
31
31
|
[(3,)]
|
|
32
32
|
"""
|
|
33
33
|
from .node import PostgresNode
|
|
34
|
+
from testgres.operations.remote_ops import ConnectionParams
|
|
35
|
+
from testgres.operations.remote_ops import RemoteOperations
|
|
36
|
+
|
|
37
|
+
import typing
|
|
34
38
|
|
|
35
39
|
|
|
36
40
|
def get_new_node(name=None, base_dir=None, **kwargs):
|
|
@@ -42,11 +46,16 @@ def get_new_node(name=None, base_dir=None, **kwargs):
|
|
|
42
46
|
return PostgresNode(name=name, base_dir=base_dir, **kwargs)
|
|
43
47
|
|
|
44
48
|
|
|
45
|
-
def get_remote_node(name=None, conn_params=None):
|
|
49
|
+
def get_remote_node(name=None, conn_params: typing.Optional[ConnectionParams] = None):
|
|
46
50
|
"""
|
|
47
51
|
Simply a wrapper around :class:`.PostgresNode` constructor for remote node.
|
|
48
52
|
See :meth:`.PostgresNode.__init__` for details.
|
|
49
53
|
For remote connection you can add the next parameter:
|
|
50
54
|
conn_params = ConnectionParams(host='127.0.0.1', ssh_key=None, username=default_username())
|
|
51
55
|
"""
|
|
52
|
-
|
|
56
|
+
|
|
57
|
+
if conn_params is None:
|
|
58
|
+
raise ValueError("Argument 'conn_params' is None.")
|
|
59
|
+
|
|
60
|
+
os_ops = RemoteOperations(conn_params)
|
|
61
|
+
return PostgresNode(name=name, os_ops=os_ops)
|
|
@@ -152,12 +152,7 @@ class NodeBackup(object):
|
|
|
152
152
|
# Build a new PostgresNode
|
|
153
153
|
assert self.original_node is not None
|
|
154
154
|
|
|
155
|
-
|
|
156
|
-
node = self.original_node.clone_with_new_name_and_base_dir(name=name, base_dir=base_dir)
|
|
157
|
-
else:
|
|
158
|
-
# For backward compatibility
|
|
159
|
-
NodeClass = self.original_node.__class__
|
|
160
|
-
node = NodeClass(name=name, base_dir=base_dir, conn_params=self.original_node.os_ops.conn_params)
|
|
155
|
+
node = self.original_node.clone_with_new_name_and_base_dir(name=name, base_dir=base_dir)
|
|
161
156
|
|
|
162
157
|
assert node is not None
|
|
163
158
|
assert type(node) is self.original_node.__class__
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import logging
|
|
5
|
-
import os
|
|
6
5
|
import signal
|
|
7
6
|
import subprocess
|
|
8
7
|
|
|
@@ -91,7 +90,6 @@ from . import utils
|
|
|
91
90
|
from .utils import \
|
|
92
91
|
PgVer, \
|
|
93
92
|
eprint, \
|
|
94
|
-
get_bin_path2, \
|
|
95
93
|
get_pg_version2, \
|
|
96
94
|
execute_utility2, \
|
|
97
95
|
options_string, \
|
|
@@ -101,7 +99,6 @@ from .raise_error import RaiseError
|
|
|
101
99
|
|
|
102
100
|
from .backup import NodeBackup
|
|
103
101
|
|
|
104
|
-
from testgres.operations.os_ops import ConnectionParams
|
|
105
102
|
from testgres.operations.os_ops import OsOperations
|
|
106
103
|
from testgres.operations.local_ops import LocalOperations
|
|
107
104
|
|
|
@@ -165,6 +162,7 @@ class PostgresNode(object):
|
|
|
165
162
|
|
|
166
163
|
_name: typing.Optional[str]
|
|
167
164
|
_port: typing.Optional[int]
|
|
165
|
+
_bin_dir: str
|
|
168
166
|
_should_free_port: bool
|
|
169
167
|
_os_ops: OsOperations
|
|
170
168
|
_port_manager: typing.Optional[PortManager]
|
|
@@ -174,8 +172,7 @@ class PostgresNode(object):
|
|
|
174
172
|
name=None,
|
|
175
173
|
base_dir=None,
|
|
176
174
|
port: typing.Optional[int] = None,
|
|
177
|
-
|
|
178
|
-
bin_dir=None,
|
|
175
|
+
bin_dir: typing.Optional[str] = None,
|
|
179
176
|
prefix=None,
|
|
180
177
|
os_ops: typing.Optional[OsOperations] = None,
|
|
181
178
|
port_manager: typing.Optional[PortManager] = None):
|
|
@@ -191,14 +188,10 @@ class PostgresNode(object):
|
|
|
191
188
|
port_manager: None or correct port manager object.
|
|
192
189
|
"""
|
|
193
190
|
assert port is None or type(port) is int
|
|
191
|
+
assert bin_dir is None or type(bin_dir) is str
|
|
194
192
|
assert os_ops is None or isinstance(os_ops, OsOperations)
|
|
195
193
|
assert port_manager is None or isinstance(port_manager, PortManager)
|
|
196
194
|
|
|
197
|
-
if conn_params is not None:
|
|
198
|
-
assert type(conn_params) is ConnectionParams
|
|
199
|
-
|
|
200
|
-
raise InvalidOperationException("conn_params is deprecated, please use os_ops parameter instead.")
|
|
201
|
-
|
|
202
195
|
# private
|
|
203
196
|
if os_ops is None:
|
|
204
197
|
self._os_ops = __class__._get_os_ops()
|
|
@@ -210,9 +203,15 @@ class PostgresNode(object):
|
|
|
210
203
|
assert self._os_ops is not None
|
|
211
204
|
assert isinstance(self._os_ops, OsOperations)
|
|
212
205
|
|
|
213
|
-
|
|
206
|
+
if bin_dir is not None:
|
|
207
|
+
self._bin_dir = bin_dir
|
|
208
|
+
else:
|
|
209
|
+
self._bin_dir = utils.get_bin_dir(self._os_ops)
|
|
210
|
+
|
|
211
|
+
assert type(self._bin_dir) is str
|
|
212
|
+
|
|
213
|
+
self._pg_version = PgVer(get_pg_version2(self._os_ops, self._bin_dir))
|
|
214
214
|
self._base_dir = base_dir
|
|
215
|
-
self._bin_dir = bin_dir
|
|
216
215
|
self._prefix = prefix
|
|
217
216
|
self._logger = None
|
|
218
217
|
self._master = None
|
|
@@ -486,18 +485,17 @@ class PostgresNode(object):
|
|
|
486
485
|
@property
|
|
487
486
|
def base_dir(self):
|
|
488
487
|
if not self._base_dir:
|
|
489
|
-
self._base_dir = self.
|
|
488
|
+
self._base_dir = self._os_ops.mkdtemp(prefix=self._prefix or TMP_NODE)
|
|
490
489
|
|
|
491
490
|
# NOTE: it's safe to create a new dir
|
|
492
|
-
if not self.
|
|
493
|
-
self.
|
|
491
|
+
if not self._os_ops.path_exists(self._base_dir):
|
|
492
|
+
self._os_ops.makedirs(self._base_dir)
|
|
494
493
|
|
|
495
494
|
return self._base_dir
|
|
496
495
|
|
|
497
496
|
@property
|
|
498
|
-
def bin_dir(self):
|
|
499
|
-
|
|
500
|
-
self._bin_dir = os.path.dirname(get_bin_path2(self.os_ops, "pg_config"))
|
|
497
|
+
def bin_dir(self) -> str:
|
|
498
|
+
assert type(self._bin_dir) is str
|
|
501
499
|
return self._bin_dir
|
|
502
500
|
|
|
503
501
|
@property
|
|
@@ -509,8 +507,8 @@ class PostgresNode(object):
|
|
|
509
507
|
assert type(path) is str
|
|
510
508
|
|
|
511
509
|
# NOTE: it's safe to create a new dir
|
|
512
|
-
if not self.
|
|
513
|
-
self.
|
|
510
|
+
if not self._os_ops.path_exists(path):
|
|
511
|
+
self._os_ops.makedirs(path)
|
|
514
512
|
|
|
515
513
|
return path
|
|
516
514
|
|
|
@@ -587,7 +585,7 @@ class PostgresNode(object):
|
|
|
587
585
|
|
|
588
586
|
ps_command = ['ps', '-o', 'pid=', '-p', str(node_pid)]
|
|
589
587
|
|
|
590
|
-
ps_output = self.
|
|
588
|
+
ps_output = self._os_ops.exec_command(cmd=ps_command, shell=True, ignore_errors=True).decode('utf-8')
|
|
591
589
|
assert type(ps_output) is str
|
|
592
590
|
|
|
593
591
|
if ps_output == "":
|
|
@@ -600,13 +598,13 @@ class PostgresNode(object):
|
|
|
600
598
|
|
|
601
599
|
try:
|
|
602
600
|
eprint('Force stopping node {0} with PID {1}'.format(self.name, node_pid))
|
|
603
|
-
self.
|
|
601
|
+
self._os_ops.kill(node_pid, signal.SIGKILL)
|
|
604
602
|
except Exception:
|
|
605
603
|
# The node has already stopped
|
|
606
604
|
pass
|
|
607
605
|
|
|
608
606
|
# Check that node stopped - print only column pid without headers
|
|
609
|
-
ps_output = self.
|
|
607
|
+
ps_output = self._os_ops.exec_command(cmd=ps_command, shell=True, ignore_errors=True).decode('utf-8')
|
|
610
608
|
assert type(ps_output) is str
|
|
611
609
|
|
|
612
610
|
if ps_output == "":
|
|
@@ -669,7 +667,7 @@ class PostgresNode(object):
|
|
|
669
667
|
|
|
670
668
|
signal_name = self._os_ops.build_path(self.data_dir, "standby.signal")
|
|
671
669
|
assert type(signal_name) is str
|
|
672
|
-
self.
|
|
670
|
+
self._os_ops.touch(signal_name)
|
|
673
671
|
else:
|
|
674
672
|
line += "standby_mode=on\n"
|
|
675
673
|
|
|
@@ -730,10 +728,10 @@ class PostgresNode(object):
|
|
|
730
728
|
|
|
731
729
|
for f, num_lines in files:
|
|
732
730
|
# skip missing files
|
|
733
|
-
if not self.
|
|
731
|
+
if not self._os_ops.path_exists(f):
|
|
734
732
|
continue
|
|
735
733
|
|
|
736
|
-
file_lines = self.
|
|
734
|
+
file_lines = self._os_ops.readlines(f, num_lines, binary=True, encoding=None)
|
|
737
735
|
lines = b''.join(file_lines)
|
|
738
736
|
|
|
739
737
|
# fill list
|
|
@@ -800,14 +798,14 @@ class PostgresNode(object):
|
|
|
800
798
|
|
|
801
799
|
# filter lines in hba file
|
|
802
800
|
# get rid of comments and blank lines
|
|
803
|
-
hba_conf_file = self.
|
|
801
|
+
hba_conf_file = self._os_ops.readlines(hba_conf)
|
|
804
802
|
lines = [
|
|
805
803
|
s for s in hba_conf_file
|
|
806
804
|
if len(s.strip()) > 0 and not s.startswith('#')
|
|
807
805
|
]
|
|
808
806
|
|
|
809
807
|
# write filtered lines
|
|
810
|
-
self.
|
|
808
|
+
self._os_ops.write(hba_conf, lines, truncate=True)
|
|
811
809
|
|
|
812
810
|
# replication-related settings
|
|
813
811
|
if allow_streaming:
|
|
@@ -819,7 +817,7 @@ class PostgresNode(object):
|
|
|
819
817
|
# get auth methods
|
|
820
818
|
auth_local = get_auth_method('local')
|
|
821
819
|
auth_host = get_auth_method('host')
|
|
822
|
-
subnet_base = ".".join(self.
|
|
820
|
+
subnet_base = ".".join(self._os_ops.host.split('.')[:-1] + ['0'])
|
|
823
821
|
|
|
824
822
|
new_lines = [
|
|
825
823
|
u"local\treplication\tall\t\t\t{}\n".format(auth_local),
|
|
@@ -832,10 +830,10 @@ class PostgresNode(object):
|
|
|
832
830
|
] # yapf: disable
|
|
833
831
|
|
|
834
832
|
# write missing lines
|
|
835
|
-
self.
|
|
833
|
+
self._os_ops.write(hba_conf, new_lines)
|
|
836
834
|
|
|
837
835
|
# overwrite config file
|
|
838
|
-
self.
|
|
836
|
+
self._os_ops.write(postgres_conf, '', truncate=True)
|
|
839
837
|
|
|
840
838
|
self.append_conf(fsync=fsync,
|
|
841
839
|
max_worker_processes=MAX_WORKER_PROCESSES,
|
|
@@ -915,7 +913,7 @@ class PostgresNode(object):
|
|
|
915
913
|
conf_text = ''
|
|
916
914
|
for line in lines:
|
|
917
915
|
conf_text += text_type(line) + '\n'
|
|
918
|
-
self.
|
|
916
|
+
self._os_ops.write(config_name, conf_text)
|
|
919
917
|
|
|
920
918
|
return self
|
|
921
919
|
|
|
@@ -948,7 +946,7 @@ class PostgresNode(object):
|
|
|
948
946
|
_params += ["-D"] if self._pg_version >= PgVer('9.5') else []
|
|
949
947
|
_params += [self.data_dir]
|
|
950
948
|
|
|
951
|
-
data = execute_utility2(self.
|
|
949
|
+
data = execute_utility2(self._os_ops, _params, self.utils_log_file)
|
|
952
950
|
|
|
953
951
|
out_dict = {}
|
|
954
952
|
|
|
@@ -958,7 +956,14 @@ class PostgresNode(object):
|
|
|
958
956
|
|
|
959
957
|
return out_dict
|
|
960
958
|
|
|
961
|
-
def slow_start(
|
|
959
|
+
def slow_start(
|
|
960
|
+
self,
|
|
961
|
+
replica: bool = False,
|
|
962
|
+
dbname: typing.Optional[str] = 'template1',
|
|
963
|
+
username: typing.Optional[str] = None,
|
|
964
|
+
max_attempts: int = 0,
|
|
965
|
+
exec_env: typing.Optional[typing.Dict[str, str]] = None,
|
|
966
|
+
):
|
|
962
967
|
"""
|
|
963
968
|
Starts the PostgreSQL instance and then polls the instance
|
|
964
969
|
until it reaches the expected state (primary or replica). The state is checked
|
|
@@ -971,6 +976,8 @@ class PostgresNode(object):
|
|
|
971
976
|
If False, waits for the instance to be in primary mode. Default is False.
|
|
972
977
|
max_attempts:
|
|
973
978
|
"""
|
|
979
|
+
assert dbname is None or type(dbname) is str
|
|
980
|
+
assert username is None or type(username) is str
|
|
974
981
|
assert exec_env is None or type(exec_env) is dict
|
|
975
982
|
|
|
976
983
|
self.start(exec_env=exec_env)
|
|
@@ -992,7 +999,7 @@ class PostgresNode(object):
|
|
|
992
999
|
self.poll_query_until(
|
|
993
1000
|
query=query,
|
|
994
1001
|
dbname=dbname,
|
|
995
|
-
username=username or self.
|
|
1002
|
+
username=username or self._os_ops.username,
|
|
996
1003
|
suppress=suppressed_exceptions,
|
|
997
1004
|
max_attempts=max_attempts,
|
|
998
1005
|
)
|
|
@@ -1094,7 +1101,7 @@ class PostgresNode(object):
|
|
|
1094
1101
|
|
|
1095
1102
|
def LOCAL__start_node():
|
|
1096
1103
|
# 'error' will be None on Windows
|
|
1097
|
-
_, _, error = execute_utility2(self.
|
|
1104
|
+
_, _, error = execute_utility2(self._os_ops, _params, self.utils_log_file, verbose=True, exec_env=exec_env)
|
|
1098
1105
|
assert error is None or type(error) is str
|
|
1099
1106
|
if error and 'does not exist' in error:
|
|
1100
1107
|
raise Exception(error)
|
|
@@ -1183,7 +1190,7 @@ class PostgresNode(object):
|
|
|
1183
1190
|
"stop"
|
|
1184
1191
|
] + params # yapf: disable
|
|
1185
1192
|
|
|
1186
|
-
execute_utility2(self.
|
|
1193
|
+
execute_utility2(self._os_ops, _params, self.utils_log_file)
|
|
1187
1194
|
|
|
1188
1195
|
self._manually_started_pm_pid = None
|
|
1189
1196
|
|
|
@@ -1207,7 +1214,10 @@ class PostgresNode(object):
|
|
|
1207
1214
|
|
|
1208
1215
|
assert x.node_status == NodeStatus.Running
|
|
1209
1216
|
assert type(x.pid) is int
|
|
1210
|
-
|
|
1217
|
+
if self._os_ops.get_platform() == "win32":
|
|
1218
|
+
sig = 21 # signal.SIGBREAK
|
|
1219
|
+
else:
|
|
1220
|
+
sig = signal.SIGKILL
|
|
1211
1221
|
if someone is None:
|
|
1212
1222
|
self._os_ops.kill(x.pid, sig)
|
|
1213
1223
|
self._manually_started_pm_pid = None
|
|
@@ -1240,7 +1250,7 @@ class PostgresNode(object):
|
|
|
1240
1250
|
] + params # yapf: disable
|
|
1241
1251
|
|
|
1242
1252
|
try:
|
|
1243
|
-
error_code, out, error = execute_utility2(self.
|
|
1253
|
+
error_code, out, error = execute_utility2(self._os_ops, _params, self.utils_log_file, verbose=True)
|
|
1244
1254
|
if error and 'could not start server' in error:
|
|
1245
1255
|
raise ExecUtilException
|
|
1246
1256
|
except ExecUtilException as e:
|
|
@@ -1269,7 +1279,7 @@ class PostgresNode(object):
|
|
|
1269
1279
|
"reload"
|
|
1270
1280
|
] + params # yapf: disable
|
|
1271
1281
|
|
|
1272
|
-
execute_utility2(self.
|
|
1282
|
+
execute_utility2(self._os_ops, _params, self.utils_log_file)
|
|
1273
1283
|
|
|
1274
1284
|
return self
|
|
1275
1285
|
|
|
@@ -1291,7 +1301,7 @@ class PostgresNode(object):
|
|
|
1291
1301
|
"promote"
|
|
1292
1302
|
] # yapf: disable
|
|
1293
1303
|
|
|
1294
|
-
execute_utility2(self.
|
|
1304
|
+
execute_utility2(self._os_ops, _params, self.utils_log_file)
|
|
1295
1305
|
|
|
1296
1306
|
# for versions below 10 `promote` is asynchronous so we need to wait
|
|
1297
1307
|
# until it actually becomes writable
|
|
@@ -1326,7 +1336,7 @@ class PostgresNode(object):
|
|
|
1326
1336
|
"-w" # wait
|
|
1327
1337
|
] + params # yapf: disable
|
|
1328
1338
|
|
|
1329
|
-
return execute_utility2(self.
|
|
1339
|
+
return execute_utility2(self._os_ops, _params, self.utils_log_file)
|
|
1330
1340
|
|
|
1331
1341
|
def release_resources(self):
|
|
1332
1342
|
"""
|
|
@@ -1362,7 +1372,7 @@ class PostgresNode(object):
|
|
|
1362
1372
|
else:
|
|
1363
1373
|
rm_dir = self.data_dir # just data, save logs
|
|
1364
1374
|
|
|
1365
|
-
self.
|
|
1375
|
+
self._os_ops.rmdirs(rm_dir, ignore_errors=False)
|
|
1366
1376
|
|
|
1367
1377
|
if release_resources:
|
|
1368
1378
|
self._release_resources()
|
|
@@ -1457,7 +1467,7 @@ class PostgresNode(object):
|
|
|
1457
1467
|
self._get_bin_path("psql"),
|
|
1458
1468
|
"-p", str(port),
|
|
1459
1469
|
"-h", host,
|
|
1460
|
-
"-U", username or self.
|
|
1470
|
+
"-U", username or self._os_ops.username,
|
|
1461
1471
|
"-d", dbname or default_dbname(),
|
|
1462
1472
|
"-X", # no .psqlrc
|
|
1463
1473
|
"-A", # unaligned output
|
|
@@ -1477,7 +1487,7 @@ class PostgresNode(object):
|
|
|
1477
1487
|
else:
|
|
1478
1488
|
raise QueryException('Query or filename must be provided')
|
|
1479
1489
|
|
|
1480
|
-
return self.
|
|
1490
|
+
return self._os_ops.exec_command(
|
|
1481
1491
|
psql_params,
|
|
1482
1492
|
verbose=True,
|
|
1483
1493
|
input=input,
|
|
@@ -1560,9 +1570,9 @@ class PostgresNode(object):
|
|
|
1560
1570
|
# Generate tmpfile or tmpdir
|
|
1561
1571
|
def tmpfile():
|
|
1562
1572
|
if format == DumpFormat.Directory:
|
|
1563
|
-
fname = self.
|
|
1573
|
+
fname = self._os_ops.mkdtemp(prefix=TMP_DUMP)
|
|
1564
1574
|
else:
|
|
1565
|
-
fname = self.
|
|
1575
|
+
fname = self._os_ops.mkstemp(prefix=TMP_DUMP)
|
|
1566
1576
|
return fname
|
|
1567
1577
|
|
|
1568
1578
|
filename = filename or tmpfile()
|
|
@@ -1572,7 +1582,7 @@ class PostgresNode(object):
|
|
|
1572
1582
|
"-p", str(self.port),
|
|
1573
1583
|
"-h", self.host,
|
|
1574
1584
|
"-f", filename,
|
|
1575
|
-
"-U", username or self.
|
|
1585
|
+
"-U", username or self._os_ops.username,
|
|
1576
1586
|
"-d", dbname or default_dbname(),
|
|
1577
1587
|
"-F", format.value
|
|
1578
1588
|
] # yapf: disable
|
|
@@ -1581,7 +1591,7 @@ class PostgresNode(object):
|
|
|
1581
1591
|
if options:
|
|
1582
1592
|
_params.extend(options)
|
|
1583
1593
|
|
|
1584
|
-
execute_utility2(self.
|
|
1594
|
+
execute_utility2(self._os_ops, _params, self.utils_log_file)
|
|
1585
1595
|
|
|
1586
1596
|
return filename
|
|
1587
1597
|
|
|
@@ -1597,7 +1607,7 @@ class PostgresNode(object):
|
|
|
1597
1607
|
|
|
1598
1608
|
# Set default arguments
|
|
1599
1609
|
dbname = dbname or default_dbname()
|
|
1600
|
-
username = username or self.
|
|
1610
|
+
username = username or self._os_ops.username
|
|
1601
1611
|
|
|
1602
1612
|
_params = [
|
|
1603
1613
|
self._get_bin_path("pg_restore"),
|
|
@@ -1610,20 +1620,22 @@ class PostgresNode(object):
|
|
|
1610
1620
|
|
|
1611
1621
|
# try pg_restore if dump is binary format, and psql if not
|
|
1612
1622
|
try:
|
|
1613
|
-
execute_utility2(self.
|
|
1623
|
+
execute_utility2(self._os_ops, _params, self.utils_log_name)
|
|
1614
1624
|
except ExecUtilException:
|
|
1615
1625
|
self.psql(filename=filename, dbname=dbname, username=username)
|
|
1616
1626
|
|
|
1617
1627
|
@method_decorator(positional_args_hack(['dbname', 'query']))
|
|
1618
|
-
def poll_query_until(
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1628
|
+
def poll_query_until(
|
|
1629
|
+
self,
|
|
1630
|
+
query,
|
|
1631
|
+
dbname: typing.Optional[str] = None,
|
|
1632
|
+
username: typing.Optional[str] = None,
|
|
1633
|
+
max_attempts: int = 0,
|
|
1634
|
+
sleep_time: typing.Union[int, float] = 1,
|
|
1635
|
+
expected: bool = True,
|
|
1636
|
+
commit: bool = True,
|
|
1637
|
+
suppress: typing.Optional[typing.Iterable[BaseException]] = None,
|
|
1638
|
+
) -> None:
|
|
1627
1639
|
"""
|
|
1628
1640
|
Run a query once per second until it returns 'expected'.
|
|
1629
1641
|
Query should return a single value (1 row, 1 column).
|
|
@@ -1650,6 +1662,8 @@ class PostgresNode(object):
|
|
|
1650
1662
|
assert max_attempts >= 0
|
|
1651
1663
|
assert type(sleep_time) in [int, float]
|
|
1652
1664
|
assert sleep_time > 0
|
|
1665
|
+
assert suppress is None or isinstance(suppress, typing.Iterable)
|
|
1666
|
+
|
|
1653
1667
|
attempts = 0
|
|
1654
1668
|
while max_attempts == 0 or attempts < max_attempts:
|
|
1655
1669
|
try:
|
|
@@ -1875,13 +1889,13 @@ class PostgresNode(object):
|
|
|
1875
1889
|
self._get_bin_path("pgbench"),
|
|
1876
1890
|
"-p", str(self.port),
|
|
1877
1891
|
"-h", self.host,
|
|
1878
|
-
"-U", username or self.
|
|
1892
|
+
"-U", username or self._os_ops.username
|
|
1879
1893
|
] + options # yapf: disable
|
|
1880
1894
|
|
|
1881
1895
|
# should be the last one
|
|
1882
1896
|
_params.append(dbname)
|
|
1883
1897
|
|
|
1884
|
-
proc = self.
|
|
1898
|
+
proc = self._os_ops.exec_command(_params, stdout=stdout, stderr=stderr, wait_exit=True, get_process=True)
|
|
1885
1899
|
|
|
1886
1900
|
return proc
|
|
1887
1901
|
|
|
@@ -1948,7 +1962,7 @@ class PostgresNode(object):
|
|
|
1948
1962
|
self._get_bin_path("pgbench"),
|
|
1949
1963
|
"-p", str(self.port),
|
|
1950
1964
|
"-h", self.host,
|
|
1951
|
-
"-U", username or self.
|
|
1965
|
+
"-U", username or self._os_ops.username
|
|
1952
1966
|
] + options # yapf: disable
|
|
1953
1967
|
|
|
1954
1968
|
for key, value in iteritems(kwargs):
|
|
@@ -1965,7 +1979,7 @@ class PostgresNode(object):
|
|
|
1965
1979
|
# should be the last one
|
|
1966
1980
|
_params.append(dbname)
|
|
1967
1981
|
|
|
1968
|
-
return execute_utility2(self.
|
|
1982
|
+
return execute_utility2(self._os_ops, _params, self.utils_log_file)
|
|
1969
1983
|
|
|
1970
1984
|
def connect(self,
|
|
1971
1985
|
dbname=None,
|
|
@@ -2054,9 +2068,9 @@ class PostgresNode(object):
|
|
|
2054
2068
|
assert isinstance(self._os_ops, OsOperations)
|
|
2055
2069
|
|
|
2056
2070
|
# parse postgresql.auto.conf
|
|
2057
|
-
path = self.
|
|
2071
|
+
path = self._os_ops.build_path(self.data_dir, config)
|
|
2058
2072
|
|
|
2059
|
-
lines = self.
|
|
2073
|
+
lines = self._os_ops.readlines(path)
|
|
2060
2074
|
current_options = {}
|
|
2061
2075
|
current_directives = []
|
|
2062
2076
|
for line in lines:
|
|
@@ -2103,7 +2117,7 @@ class PostgresNode(object):
|
|
|
2103
2117
|
for directive in current_directives:
|
|
2104
2118
|
auto_conf += directive + "\n"
|
|
2105
2119
|
|
|
2106
|
-
self.
|
|
2120
|
+
self._os_ops.write(path, auto_conf, truncate=True)
|
|
2107
2121
|
|
|
2108
2122
|
def upgrade_from(self, old_node, options=None, expect_error=False):
|
|
2109
2123
|
"""
|
|
@@ -2138,7 +2152,7 @@ class PostgresNode(object):
|
|
|
2138
2152
|
]
|
|
2139
2153
|
upgrade_command += options
|
|
2140
2154
|
|
|
2141
|
-
return self.
|
|
2155
|
+
return self._os_ops.exec_command(upgrade_command, expect_error=expect_error)
|
|
2142
2156
|
|
|
2143
2157
|
def _release_resources(self):
|
|
2144
2158
|
self._free_port()
|
|
@@ -2162,12 +2176,9 @@ class PostgresNode(object):
|
|
|
2162
2176
|
def _get_bin_path(self, filename):
|
|
2163
2177
|
assert self._os_ops is not None
|
|
2164
2178
|
assert isinstance(self._os_ops, OsOperations)
|
|
2179
|
+
assert type(self._bin_dir) is str
|
|
2165
2180
|
|
|
2166
|
-
|
|
2167
|
-
bin_path = self._os_ops.build_path(self.bin_dir, filename)
|
|
2168
|
-
else:
|
|
2169
|
-
bin_path = get_bin_path2(self.os_ops, filename)
|
|
2170
|
-
return bin_path
|
|
2181
|
+
return self._os_ops.build_path(self._bin_dir, filename)
|
|
2171
2182
|
|
|
2172
2183
|
@staticmethod
|
|
2173
2184
|
def _escape_config_value(value):
|
|
@@ -3,9 +3,6 @@ from .node import LocalOperations
|
|
|
3
3
|
from .node import PostgresNode
|
|
4
4
|
from .node import PortManager
|
|
5
5
|
|
|
6
|
-
import os
|
|
7
|
-
import platform
|
|
8
|
-
import tempfile
|
|
9
6
|
import typing
|
|
10
7
|
|
|
11
8
|
|
|
@@ -16,7 +13,7 @@ T_LIST_STR = typing.List[str]
|
|
|
16
13
|
class NodeApp:
|
|
17
14
|
_test_path: str
|
|
18
15
|
_os_ops: OsOperations
|
|
19
|
-
_port_manager: PortManager
|
|
16
|
+
_port_manager: typing.Optional[PortManager]
|
|
20
17
|
_nodes_to_cleanup: typing.List[PostgresNode]
|
|
21
18
|
|
|
22
19
|
def __init__(
|
|
@@ -39,7 +36,7 @@ class NodeApp:
|
|
|
39
36
|
|
|
40
37
|
if test_path is None:
|
|
41
38
|
self._test_path = os_ops.cwd()
|
|
42
|
-
elif
|
|
39
|
+
elif self._os_ops.is_abs_path(test_path):
|
|
43
40
|
self._test_path = test_path
|
|
44
41
|
else:
|
|
45
42
|
self._test_path = os_ops.build_path(os_ops.cwd(), test_path)
|
|
@@ -60,7 +57,7 @@ class NodeApp:
|
|
|
60
57
|
return self._os_ops
|
|
61
58
|
|
|
62
59
|
@property
|
|
63
|
-
def port_manager(self) -> PortManager:
|
|
60
|
+
def port_manager(self) -> typing.Optional[PortManager]:
|
|
64
61
|
assert self._port_manager is None or isinstance(self._port_manager, PortManager)
|
|
65
62
|
return self._port_manager
|
|
66
63
|
|
|
@@ -158,6 +155,8 @@ class NodeApp:
|
|
|
158
155
|
|
|
159
156
|
# set major version
|
|
160
157
|
pg_version_file = self._os_ops.read(self._os_ops.build_path(node.data_dir, 'PG_VERSION'))
|
|
158
|
+
|
|
159
|
+
# What is it ???
|
|
161
160
|
node.major_version_str = str(pg_version_file.rstrip())
|
|
162
161
|
node.major_version = float(node.major_version_str)
|
|
163
162
|
|
|
@@ -200,7 +199,7 @@ class NodeApp:
|
|
|
200
199
|
|
|
201
200
|
# Define delayed propertyes
|
|
202
201
|
if "unix_socket_directories" not in options.keys():
|
|
203
|
-
options["unix_socket_directories"] =
|
|
202
|
+
options["unix_socket_directories"] = self._gettempdir_for_socket()
|
|
204
203
|
|
|
205
204
|
# Set config values
|
|
206
205
|
node.set_auto_conf(options)
|
|
@@ -260,49 +259,50 @@ class NodeApp:
|
|
|
260
259
|
return updated_params
|
|
261
260
|
return __class__._paramlist_append(user_params, updated_params, param)
|
|
262
261
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
platform_system_name = platform.system().lower()
|
|
266
|
-
|
|
267
|
-
if platform_system_name == "windows":
|
|
268
|
-
return __class__._gettempdir()
|
|
269
|
-
|
|
270
|
-
#
|
|
271
|
-
# [2025-02-17] Hot fix.
|
|
272
|
-
#
|
|
273
|
-
# Let's use hard coded path as Postgres likes.
|
|
274
|
-
#
|
|
275
|
-
# pg_config_manual.h:
|
|
276
|
-
#
|
|
277
|
-
# #ifndef WIN32
|
|
278
|
-
# #define DEFAULT_PGSOCKET_DIR "/tmp"
|
|
279
|
-
# #else
|
|
280
|
-
# #define DEFAULT_PGSOCKET_DIR ""
|
|
281
|
-
# #endif
|
|
282
|
-
#
|
|
283
|
-
# On the altlinux-10 tempfile.gettempdir() may return
|
|
284
|
-
# the path to "private" temp directiry - "/temp/.private/<username>/"
|
|
285
|
-
#
|
|
286
|
-
# But Postgres want to find a socket file in "/tmp" (see above).
|
|
287
|
-
#
|
|
262
|
+
def _gettempdir_for_socket(self) -> str:
|
|
263
|
+
assert isinstance(self._os_ops, OsOperations)
|
|
288
264
|
|
|
289
|
-
|
|
265
|
+
platform_name = self._os_ops.get_platform()
|
|
266
|
+
|
|
267
|
+
if platform_name == "linux":
|
|
268
|
+
#
|
|
269
|
+
# [2025-02-17] Hot fix.
|
|
270
|
+
#
|
|
271
|
+
# Let's use hard coded path as Postgres likes.
|
|
272
|
+
#
|
|
273
|
+
# pg_config_manual.h:
|
|
274
|
+
#
|
|
275
|
+
# #ifndef WIN32
|
|
276
|
+
# #define DEFAULT_PGSOCKET_DIR "/tmp"
|
|
277
|
+
# #else
|
|
278
|
+
# #define DEFAULT_PGSOCKET_DIR ""
|
|
279
|
+
# #endif
|
|
280
|
+
#
|
|
281
|
+
# On the altlinux-10 tempfile.gettempdir() may return
|
|
282
|
+
# the path to "private" temp directiry - "/temp/.private/<username>/"
|
|
283
|
+
#
|
|
284
|
+
# But Postgres want to find a socket file in "/tmp" (see above).
|
|
285
|
+
#
|
|
286
|
+
return "/tmp"
|
|
287
|
+
|
|
288
|
+
return self._gettempdir()
|
|
289
|
+
|
|
290
|
+
def _gettempdir(self) -> str:
|
|
291
|
+
assert isinstance(self._os_ops, OsOperations)
|
|
290
292
|
|
|
291
|
-
|
|
292
|
-
def _gettempdir() -> str:
|
|
293
|
-
v = tempfile.gettempdir()
|
|
293
|
+
v = self._os_ops.get_tempdir()
|
|
294
294
|
|
|
295
295
|
#
|
|
296
296
|
# Paranoid checks
|
|
297
297
|
#
|
|
298
298
|
if type(v) is str:
|
|
299
|
-
__class__._raise_bugcheck("
|
|
299
|
+
__class__._raise_bugcheck("os_ops.get_tempdir returned a value with type {0}.".format(type(v).__name__))
|
|
300
300
|
|
|
301
301
|
if v == "":
|
|
302
|
-
__class__._raise_bugcheck("
|
|
302
|
+
__class__._raise_bugcheck("os_ops.get_tempdir returned an empty string.")
|
|
303
303
|
|
|
304
|
-
if not
|
|
305
|
-
__class__._raise_bugcheck("
|
|
304
|
+
if not self._os_ops.path_exists(v):
|
|
305
|
+
__class__._raise_bugcheck("os_ops.get_tempdir returned a not exist path [{0}].".format(v))
|
|
306
306
|
|
|
307
307
|
# OK
|
|
308
308
|
return v
|
|
@@ -140,7 +140,7 @@ def get_bin_path2(os_ops: OsOperations, filename):
|
|
|
140
140
|
assert isinstance(os_ops, OsOperations)
|
|
141
141
|
|
|
142
142
|
# check if it's already absolute
|
|
143
|
-
if
|
|
143
|
+
if os_ops.is_abs_path(filename):
|
|
144
144
|
return filename
|
|
145
145
|
if isinstance(os_ops, RemoteOperations):
|
|
146
146
|
pg_config = os.environ.get("PG_CONFIG_REMOTE") or os.environ.get("PG_CONFIG")
|
|
@@ -165,6 +165,33 @@ def get_bin_path2(os_ops: OsOperations, filename):
|
|
|
165
165
|
return filename
|
|
166
166
|
|
|
167
167
|
|
|
168
|
+
def get_bin_dir(os_ops: OsOperations) -> str:
|
|
169
|
+
assert os_ops is not None
|
|
170
|
+
assert isinstance(os_ops, OsOperations)
|
|
171
|
+
|
|
172
|
+
if isinstance(os_ops, RemoteOperations):
|
|
173
|
+
pg_config = os.environ.get("PG_CONFIG_REMOTE") or os.environ.get("PG_CONFIG")
|
|
174
|
+
else:
|
|
175
|
+
# try PG_CONFIG - get from local machine
|
|
176
|
+
pg_config = os.environ.get("PG_CONFIG")
|
|
177
|
+
|
|
178
|
+
if pg_config:
|
|
179
|
+
bindir = get_pg_config(pg_config, os_ops)["BINDIR"]
|
|
180
|
+
return bindir
|
|
181
|
+
|
|
182
|
+
# try PG_BIN
|
|
183
|
+
pg_bin = os_ops.environ("PG_BIN")
|
|
184
|
+
if pg_bin:
|
|
185
|
+
return pg_bin
|
|
186
|
+
|
|
187
|
+
pg_config_path = os_ops.find_executable('pg_config')
|
|
188
|
+
if pg_config_path:
|
|
189
|
+
bindir = get_pg_config(pg_config_path)["BINDIR"]
|
|
190
|
+
return bindir
|
|
191
|
+
|
|
192
|
+
raise RuntimeError("BinDir is not detected.")
|
|
193
|
+
|
|
194
|
+
|
|
168
195
|
def get_pg_config(pg_config_path=None, os_ops=None):
|
|
169
196
|
"""
|
|
170
197
|
Return output of pg_config (provided that it is installed).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: testgres
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.14.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,11 +27,10 @@ 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<3.0.0,>=2.
|
|
30
|
+
Requires-Dist: testgres.os_ops<3.0.0,>=2.3.0
|
|
31
31
|
Dynamic: license-file
|
|
32
32
|
|
|
33
|
-
[](https://codecov.io/gh/postgrespro/testgres)
|
|
33
|
+
[](https://github.com/postgrespro/testgres/actions/workflows/ci.yml)
|
|
35
34
|
[](https://badge.fury.io/py/testgres)
|
|
36
35
|
[](https://pypi.org/project/testgres)
|
|
37
36
|
[](https://pypi.org/project/testgres)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from src import api as testgres_api
|
|
2
|
+
from src.node import PostgresNode
|
|
3
|
+
|
|
4
|
+
from tests.helpers.global_data import OsOpsDescrs
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestAPI:
|
|
8
|
+
def test_001__get_new_node(self):
|
|
9
|
+
C_NODE_NAME = "abc"
|
|
10
|
+
|
|
11
|
+
with testgres_api.get_new_node(name=C_NODE_NAME) as node:
|
|
12
|
+
assert type(node) is PostgresNode
|
|
13
|
+
assert node.name == C_NODE_NAME
|
|
14
|
+
node.init()
|
|
15
|
+
node.slow_start()
|
|
16
|
+
node.stop()
|
|
17
|
+
return
|
|
18
|
+
|
|
19
|
+
def test_001__get_remote_node(self):
|
|
20
|
+
C_NODE_NAME = "abc"
|
|
21
|
+
|
|
22
|
+
conn_params = OsOpsDescrs.sm_remote_conn_params
|
|
23
|
+
|
|
24
|
+
with testgres_api.get_remote_node(name=C_NODE_NAME, conn_params=conn_params) as node:
|
|
25
|
+
assert type(node) is PostgresNode
|
|
26
|
+
assert node.name == C_NODE_NAME
|
|
27
|
+
node.init()
|
|
28
|
+
node.slow_start()
|
|
29
|
+
node.stop()
|
|
30
|
+
return
|
|
@@ -94,8 +94,8 @@ class TestTestgresCommon:
|
|
|
94
94
|
|
|
95
95
|
# Author: Mark G.
|
|
96
96
|
assert v.major == 1
|
|
97
|
-
assert v.minor ==
|
|
98
|
-
assert v.micro ==
|
|
97
|
+
assert v.minor == 14
|
|
98
|
+
assert v.micro == 0
|
|
99
99
|
|
|
100
100
|
assert str(v) == testgres_version
|
|
101
101
|
return
|
|
@@ -2422,13 +2422,14 @@ where c.relname=%s;"""
|
|
|
2422
2422
|
class tag_rmdirs_protector:
|
|
2423
2423
|
_os_ops: OsOperations
|
|
2424
2424
|
_cwd: str
|
|
2425
|
-
_old_rmdirs:
|
|
2425
|
+
_old_rmdirs: typing.Optional[typing.Callable]
|
|
2426
2426
|
_cwd: str
|
|
2427
2427
|
|
|
2428
2428
|
def __init__(self, os_ops: OsOperations):
|
|
2429
2429
|
self._os_ops = os_ops
|
|
2430
2430
|
self._cwd = os.path.abspath(os_ops.cwd())
|
|
2431
2431
|
self._old_rmdirs = os_ops.rmdirs
|
|
2432
|
+
return
|
|
2432
2433
|
|
|
2433
2434
|
def __enter__(self):
|
|
2434
2435
|
assert self._os_ops.rmdirs == self._old_rmdirs
|
|
@@ -2437,6 +2438,7 @@ where c.relname=%s;"""
|
|
|
2437
2438
|
|
|
2438
2439
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
2439
2440
|
assert self._os_ops.rmdirs == self.proxy__rmdirs
|
|
2441
|
+
assert isinstance(self._old_rmdirs, typing.Callable)
|
|
2440
2442
|
self._os_ops.rmdirs = self._old_rmdirs
|
|
2441
2443
|
return False
|
|
2442
2444
|
|
|
@@ -2465,7 +2467,7 @@ where c.relname=%s;"""
|
|
|
2465
2467
|
assert node_app.os_ops is os_ops
|
|
2466
2468
|
|
|
2467
2469
|
with pytest.raises(expected_exception=BaseException) as x:
|
|
2468
|
-
node_app.make_empty(base_dir=None)
|
|
2470
|
+
node_app.make_empty(base_dir=None) # type: ignore
|
|
2469
2471
|
|
|
2470
2472
|
if type(x.value) is AssertionError:
|
|
2471
2473
|
pass
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|