testgres 1.11.0__tar.gz → 1.11.1__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.11.0/testgres.egg-info → testgres-1.11.1}/PKG-INFO +2 -2
- {testgres-1.11.0 → testgres-1.11.1}/README.md +1 -1
- {testgres-1.11.0 → testgres-1.11.1}/setup.py +2 -2
- {testgres-1.11.0 → testgres-1.11.1}/testgres/__init__.py +5 -3
- {testgres-1.11.0 → testgres-1.11.1}/testgres/backup.py +17 -13
- {testgres-1.11.0 → testgres-1.11.1}/testgres/cache.py +8 -6
- {testgres-1.11.0 → testgres-1.11.1}/testgres/config.py +2 -1
- testgres-1.11.0/testgres/port_manager.py → testgres-1.11.1/testgres/impl/port_manager__generic.py +38 -44
- testgres-1.11.1/testgres/impl/port_manager__this_host.py +33 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/node.py +283 -230
- testgres-1.11.1/testgres/node_app.py +317 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/operations/local_ops.py +108 -9
- {testgres-1.11.0 → testgres-1.11.1}/testgres/operations/os_ops.py +25 -4
- {testgres-1.11.0 → testgres-1.11.1}/testgres/operations/remote_ops.py +166 -33
- {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/app.py +65 -22
- {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/gdb.py +0 -8
- {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/init_helpers.py +2 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/tests/test_basic.py +23 -14
- {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/setup.py +1 -1
- testgres-1.11.1/testgres/port_manager.py +10 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/utils.py +25 -34
- {testgres-1.11.0 → testgres-1.11.1/testgres.egg-info}/PKG-INFO +2 -2
- {testgres-1.11.0 → testgres-1.11.1}/testgres.egg-info/SOURCES.txt +3 -0
- {testgres-1.11.0 → testgres-1.11.1}/tests/conftest.py +308 -142
- {testgres-1.11.0 → testgres-1.11.1}/tests/helpers/global_data.py +9 -9
- {testgres-1.11.0 → testgres-1.11.1}/tests/test_config.py +5 -5
- {testgres-1.11.0 → testgres-1.11.1}/tests/test_os_ops_common.py +327 -2
- {testgres-1.11.0 → testgres-1.11.1}/tests/test_os_ops_remote.py +1 -1
- {testgres-1.11.0 → testgres-1.11.1}/tests/test_testgres_common.py +484 -31
- {testgres-1.11.0 → testgres-1.11.1}/tests/test_testgres_local.py +25 -25
- {testgres-1.11.0 → testgres-1.11.1}/tests/test_testgres_remote.py +7 -8
- {testgres-1.11.0 → testgres-1.11.1}/tests/test_utils.py +3 -3
- {testgres-1.11.0 → testgres-1.11.1}/LICENSE +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/MANIFEST.in +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/setup.cfg +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/api.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/connection.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/consts.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/decorators.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/defaults.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/enums.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/exceptions.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/logger.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/operations/__init__.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/operations/helpers.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/operations/raise_error.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/__init__.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/__init__.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/__init__.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/storage/__init__.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/storage/fs_backup.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/tests/__init__.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/pubsub.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres/standby.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres.egg-info/dependency_links.txt +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres.egg-info/requires.txt +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/testgres.egg-info/top_level.txt +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/tests/__init__.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/tests/helpers/__init__.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/tests/helpers/run_conditions.py +0 -0
- {testgres-1.11.0 → testgres-1.11.1}/tests/test_os_ops_local.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: testgres
|
|
3
|
-
Version: 1.11.
|
|
3
|
+
Version: 1.11.1
|
|
4
4
|
Summary: Testing utility for PostgreSQL and its extensions
|
|
5
5
|
Home-page: https://github.com/postgrespro/testgres
|
|
6
6
|
Author: Postgres Professional
|
|
@@ -33,7 +33,7 @@ Dynamic: summary
|
|
|
33
33
|
|
|
34
34
|
# testgres
|
|
35
35
|
|
|
36
|
-
PostgreSQL testing utility.
|
|
36
|
+
PostgreSQL testing utility. Python 3.7.17+ is supported.
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
## Installation
|
|
@@ -27,9 +27,9 @@ with open('README.md', 'r') as f:
|
|
|
27
27
|
readme = f.read()
|
|
28
28
|
|
|
29
29
|
setup(
|
|
30
|
-
version='1.11.
|
|
30
|
+
version='1.11.1',
|
|
31
31
|
name='testgres',
|
|
32
|
-
packages=['testgres', 'testgres.operations'],
|
|
32
|
+
packages=['testgres', 'testgres.operations', 'testgres.impl'],
|
|
33
33
|
description='Testing utility for PostgreSQL and its extensions',
|
|
34
34
|
url='https://github.com/postgrespro/testgres',
|
|
35
35
|
long_description=readme,
|
|
@@ -33,8 +33,9 @@ from .enums import \
|
|
|
33
33
|
ProcessType, \
|
|
34
34
|
DumpFormat
|
|
35
35
|
|
|
36
|
-
from .node import PostgresNode
|
|
36
|
+
from .node import PostgresNode
|
|
37
37
|
from .node import PortManager
|
|
38
|
+
from .node_app import NodeApp
|
|
38
39
|
|
|
39
40
|
from .utils import \
|
|
40
41
|
reserve_port, \
|
|
@@ -62,8 +63,9 @@ __all__ = [
|
|
|
62
63
|
"NodeConnection", "DatabaseError", "InternalError", "ProgrammingError", "OperationalError",
|
|
63
64
|
"TestgresException", "ExecUtilException", "QueryException", "TimeoutException", "CatchUpException", "StartNodeException", "InitNodeException", "BackupException", "InvalidOperationException",
|
|
64
65
|
"XLogMethod", "IsolationLevel", "NodeStatus", "ProcessType", "DumpFormat",
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
NodeApp.__name__,
|
|
67
|
+
PostgresNode.__name__,
|
|
68
|
+
PortManager.__name__,
|
|
67
69
|
"reserve_port", "release_port", "bound_ports", "get_bin_path", "get_pg_config", "get_pg_version",
|
|
68
70
|
"First", "Any",
|
|
69
71
|
"OsOperations", "LocalOperations", "RemoteOperations", "ConnectionParams"
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
3
|
from six import raise_from
|
|
6
4
|
|
|
7
5
|
from .enums import XLogMethod
|
|
@@ -29,7 +27,9 @@ class NodeBackup(object):
|
|
|
29
27
|
"""
|
|
30
28
|
@property
|
|
31
29
|
def log_file(self):
|
|
32
|
-
|
|
30
|
+
assert self.os_ops is not None
|
|
31
|
+
assert isinstance(self.os_ops, OsOperations)
|
|
32
|
+
return self.os_ops.build_path(self.base_dir, BACKUP_LOG_FILE)
|
|
33
33
|
|
|
34
34
|
def __init__(self,
|
|
35
35
|
node,
|
|
@@ -75,7 +75,7 @@ class NodeBackup(object):
|
|
|
75
75
|
# private
|
|
76
76
|
self._available = True
|
|
77
77
|
|
|
78
|
-
data_dir =
|
|
78
|
+
data_dir = self.os_ops.build_path(self.base_dir, DATA_DIR)
|
|
79
79
|
|
|
80
80
|
_params = [
|
|
81
81
|
get_bin_path2(self.os_ops, "pg_basebackup"),
|
|
@@ -112,10 +112,13 @@ class NodeBackup(object):
|
|
|
112
112
|
available = not destroy
|
|
113
113
|
|
|
114
114
|
if available:
|
|
115
|
+
assert self.os_ops is not None
|
|
116
|
+
assert isinstance(self.os_ops, OsOperations)
|
|
117
|
+
|
|
115
118
|
dest_base_dir = self.os_ops.mkdtemp(prefix=TMP_NODE)
|
|
116
119
|
|
|
117
|
-
data1 =
|
|
118
|
-
data2 =
|
|
120
|
+
data1 = self.os_ops.build_path(self.base_dir, DATA_DIR)
|
|
121
|
+
data2 = self.os_ops.build_path(dest_base_dir, DATA_DIR)
|
|
119
122
|
|
|
120
123
|
try:
|
|
121
124
|
# Copy backup to new data dir
|
|
@@ -160,10 +163,6 @@ class NodeBackup(object):
|
|
|
160
163
|
assert type(node) == self.original_node.__class__ # noqa: E721
|
|
161
164
|
|
|
162
165
|
with clean_on_error(node) as node:
|
|
163
|
-
|
|
164
|
-
# New nodes should always remove dir tree
|
|
165
|
-
node._should_rm_dirs = True
|
|
166
|
-
|
|
167
166
|
# Set a new port
|
|
168
167
|
node.append_conf(filename=PG_CONF_FILE, line='\n')
|
|
169
168
|
node.append_conf(filename=PG_CONF_FILE, port=node.port)
|
|
@@ -184,14 +183,19 @@ class NodeBackup(object):
|
|
|
184
183
|
"""
|
|
185
184
|
|
|
186
185
|
# Build a new PostgresNode
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
node = self.spawn_primary(name=name, destroy=destroy)
|
|
187
|
+
assert node is not None
|
|
189
188
|
|
|
189
|
+
try:
|
|
190
190
|
# Assign it a master and a recovery file (private magic)
|
|
191
191
|
node._assign_master(self.original_node)
|
|
192
192
|
node._create_recovery_conf(username=self.username, slot=slot)
|
|
193
|
+
except: # noqa: E722
|
|
194
|
+
# TODO: Pass 'final=True' ?
|
|
195
|
+
node.cleanup(release_resources=True)
|
|
196
|
+
raise
|
|
193
197
|
|
|
194
|
-
|
|
198
|
+
return node
|
|
195
199
|
|
|
196
200
|
def cleanup(self):
|
|
197
201
|
"""
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
3
|
from six import raise_from
|
|
6
4
|
|
|
7
5
|
from .config import testgres_config
|
|
@@ -22,12 +20,16 @@ from .operations.local_ops import LocalOperations
|
|
|
22
20
|
from .operations.os_ops import OsOperations
|
|
23
21
|
|
|
24
22
|
|
|
25
|
-
def cached_initdb(data_dir, logfile=None, params=None, os_ops: OsOperations =
|
|
23
|
+
def cached_initdb(data_dir, logfile=None, params=None, os_ops: OsOperations = None, bin_path=None, cached=True):
|
|
26
24
|
"""
|
|
27
25
|
Perform initdb or use cached node files.
|
|
28
26
|
"""
|
|
29
27
|
|
|
30
|
-
assert os_ops is
|
|
28
|
+
assert os_ops is None or isinstance(os_ops, OsOperations)
|
|
29
|
+
|
|
30
|
+
if os_ops is None:
|
|
31
|
+
os_ops = LocalOperations.get_single_instance()
|
|
32
|
+
|
|
31
33
|
assert isinstance(os_ops, OsOperations)
|
|
32
34
|
|
|
33
35
|
def make_utility_path(name):
|
|
@@ -35,7 +37,7 @@ def cached_initdb(data_dir, logfile=None, params=None, os_ops: OsOperations = Lo
|
|
|
35
37
|
assert type(name) == str # noqa: E721
|
|
36
38
|
|
|
37
39
|
if bin_path:
|
|
38
|
-
return
|
|
40
|
+
return os_ops.build_path(bin_path, name)
|
|
39
41
|
|
|
40
42
|
return get_bin_path2(os_ops, name)
|
|
41
43
|
|
|
@@ -68,7 +70,7 @@ def cached_initdb(data_dir, logfile=None, params=None, os_ops: OsOperations = Lo
|
|
|
68
70
|
# XXX: write new unique system id to control file
|
|
69
71
|
# Some users might rely upon unique system ids, but
|
|
70
72
|
# our initdb caching mechanism breaks this contract.
|
|
71
|
-
pg_control =
|
|
73
|
+
pg_control = os_ops.build_path(data_dir, XLOG_CONTROL_FILE)
|
|
72
74
|
system_id = generate_system_id()
|
|
73
75
|
cur_pg_control = os_ops.read(pg_control, binary=True)
|
|
74
76
|
new_pg_control = system_id + cur_pg_control[len(system_id):]
|
|
@@ -50,8 +50,9 @@ class GlobalConfig(object):
|
|
|
50
50
|
_cached_initdb_dir = None
|
|
51
51
|
""" underlying class attribute for cached_initdb_dir property """
|
|
52
52
|
|
|
53
|
-
os_ops = LocalOperations()
|
|
53
|
+
os_ops = LocalOperations.get_single_instance()
|
|
54
54
|
""" OsOperation object that allows work on remote host """
|
|
55
|
+
|
|
55
56
|
@property
|
|
56
57
|
def cached_initdb_dir(self):
|
|
57
58
|
""" path to a temp directory for cached initdb. """
|
testgres-1.11.0/testgres/port_manager.py → testgres-1.11.1/testgres/impl/port_manager__generic.py
RENAMED
|
@@ -1,53 +1,18 @@
|
|
|
1
|
-
from
|
|
1
|
+
from ..operations.os_ops import OsOperations
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from . import utils
|
|
3
|
+
from ..port_manager import PortManager
|
|
4
|
+
from ..exceptions import PortForException
|
|
6
5
|
|
|
7
6
|
import threading
|
|
8
7
|
import random
|
|
9
8
|
import typing
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class PortManager:
|
|
13
|
-
def __init__(self):
|
|
14
|
-
super().__init__()
|
|
15
|
-
|
|
16
|
-
def reserve_port(self) -> int:
|
|
17
|
-
raise NotImplementedError("PortManager::reserve_port is not implemented.")
|
|
18
|
-
|
|
19
|
-
def release_port(self, number: int) -> None:
|
|
20
|
-
assert type(number) == int # noqa: E721
|
|
21
|
-
raise NotImplementedError("PortManager::release_port is not implemented.")
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class PortManager__ThisHost(PortManager):
|
|
25
|
-
sm_single_instance: PortManager = None
|
|
26
|
-
sm_single_instance_guard = threading.Lock()
|
|
27
|
-
|
|
28
|
-
def __init__(self):
|
|
29
|
-
pass
|
|
30
|
-
|
|
31
|
-
def __new__(cls) -> PortManager:
|
|
32
|
-
assert __class__ == PortManager__ThisHost
|
|
33
|
-
assert __class__.sm_single_instance_guard is not None
|
|
34
|
-
|
|
35
|
-
if __class__.sm_single_instance is None:
|
|
36
|
-
with __class__.sm_single_instance_guard:
|
|
37
|
-
__class__.sm_single_instance = super().__new__(cls)
|
|
38
|
-
assert __class__.sm_single_instance
|
|
39
|
-
assert type(__class__.sm_single_instance) == __class__ # noqa: E721
|
|
40
|
-
return __class__.sm_single_instance
|
|
41
|
-
|
|
42
|
-
def reserve_port(self) -> int:
|
|
43
|
-
return utils.reserve_port()
|
|
44
|
-
|
|
45
|
-
def release_port(self, number: int) -> None:
|
|
46
|
-
assert type(number) == int # noqa: E721
|
|
47
|
-
return utils.release_port(number)
|
|
9
|
+
import logging
|
|
48
10
|
|
|
49
11
|
|
|
50
12
|
class PortManager__Generic(PortManager):
|
|
13
|
+
_C_MIN_PORT_NUMBER = 1024
|
|
14
|
+
_C_MAX_PORT_NUMBER = 65535
|
|
15
|
+
|
|
51
16
|
_os_ops: OsOperations
|
|
52
17
|
_guard: object
|
|
53
18
|
# TODO: is there better to use bitmap fot _available_ports?
|
|
@@ -55,12 +20,21 @@ class PortManager__Generic(PortManager):
|
|
|
55
20
|
_reserved_ports: typing.Set[int]
|
|
56
21
|
|
|
57
22
|
def __init__(self, os_ops: OsOperations):
|
|
23
|
+
assert __class__._C_MIN_PORT_NUMBER <= __class__._C_MAX_PORT_NUMBER
|
|
24
|
+
|
|
58
25
|
assert os_ops is not None
|
|
59
26
|
assert isinstance(os_ops, OsOperations)
|
|
60
27
|
self._os_ops = os_ops
|
|
61
28
|
self._guard = threading.Lock()
|
|
62
|
-
|
|
63
|
-
self.
|
|
29
|
+
|
|
30
|
+
self._available_ports = set(
|
|
31
|
+
range(__class__._C_MIN_PORT_NUMBER, __class__._C_MAX_PORT_NUMBER + 1)
|
|
32
|
+
)
|
|
33
|
+
assert len(self._available_ports) == (
|
|
34
|
+
(__class__._C_MAX_PORT_NUMBER - __class__._C_MIN_PORT_NUMBER) + 1
|
|
35
|
+
)
|
|
36
|
+
self._reserved_ports = set()
|
|
37
|
+
return
|
|
64
38
|
|
|
65
39
|
def reserve_port(self) -> int:
|
|
66
40
|
assert self._guard is not None
|
|
@@ -74,9 +48,13 @@ class PortManager__Generic(PortManager):
|
|
|
74
48
|
t = None
|
|
75
49
|
|
|
76
50
|
for port in sampled_ports:
|
|
51
|
+
assert type(port) == int # noqa: E721
|
|
77
52
|
assert not (port in self._reserved_ports)
|
|
78
53
|
assert port in self._available_ports
|
|
79
54
|
|
|
55
|
+
assert port >= __class__._C_MIN_PORT_NUMBER
|
|
56
|
+
assert port <= __class__._C_MAX_PORT_NUMBER
|
|
57
|
+
|
|
80
58
|
if not self._os_ops.is_port_free(port):
|
|
81
59
|
continue
|
|
82
60
|
|
|
@@ -84,12 +62,15 @@ class PortManager__Generic(PortManager):
|
|
|
84
62
|
self._available_ports.discard(port)
|
|
85
63
|
assert port in self._reserved_ports
|
|
86
64
|
assert not (port in self._available_ports)
|
|
65
|
+
__class__.helper__send_debug_msg("Port {} is reserved.", port)
|
|
87
66
|
return port
|
|
88
67
|
|
|
89
68
|
raise PortForException("Can't select a port.")
|
|
90
69
|
|
|
91
70
|
def release_port(self, number: int) -> None:
|
|
92
71
|
assert type(number) == int # noqa: E721
|
|
72
|
+
assert number >= __class__._C_MIN_PORT_NUMBER
|
|
73
|
+
assert number <= __class__._C_MAX_PORT_NUMBER
|
|
93
74
|
|
|
94
75
|
assert self._guard is not None
|
|
95
76
|
assert type(self._reserved_ports) == set # noqa: E721
|
|
@@ -101,3 +82,16 @@ class PortManager__Generic(PortManager):
|
|
|
101
82
|
self._reserved_ports.discard(number)
|
|
102
83
|
assert not (number in self._reserved_ports)
|
|
103
84
|
assert number in self._available_ports
|
|
85
|
+
__class__.helper__send_debug_msg("Port {} is released.", number)
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def helper__send_debug_msg(msg_template: str, *args) -> None:
|
|
90
|
+
assert msg_template is not None
|
|
91
|
+
assert args is not None
|
|
92
|
+
assert type(msg_template) == str # noqa: E721
|
|
93
|
+
assert type(args) == tuple # noqa: E721
|
|
94
|
+
assert msg_template != ""
|
|
95
|
+
s = "[port manager] "
|
|
96
|
+
s += msg_template.format(*args)
|
|
97
|
+
logging.debug(s)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from ..port_manager import PortManager
|
|
2
|
+
|
|
3
|
+
from .. import utils
|
|
4
|
+
|
|
5
|
+
import threading
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PortManager__ThisHost(PortManager):
|
|
9
|
+
sm_single_instance: PortManager = None
|
|
10
|
+
sm_single_instance_guard = threading.Lock()
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def get_single_instance() -> PortManager:
|
|
14
|
+
assert __class__ == PortManager__ThisHost
|
|
15
|
+
assert __class__.sm_single_instance_guard is not None
|
|
16
|
+
|
|
17
|
+
if __class__.sm_single_instance is not None:
|
|
18
|
+
assert type(__class__.sm_single_instance) == __class__ # noqa: E721
|
|
19
|
+
return __class__.sm_single_instance
|
|
20
|
+
|
|
21
|
+
with __class__.sm_single_instance_guard:
|
|
22
|
+
if __class__.sm_single_instance is None:
|
|
23
|
+
__class__.sm_single_instance = __class__()
|
|
24
|
+
assert __class__.sm_single_instance is not None
|
|
25
|
+
assert type(__class__.sm_single_instance) == __class__ # noqa: E721
|
|
26
|
+
return __class__.sm_single_instance
|
|
27
|
+
|
|
28
|
+
def reserve_port(self) -> int:
|
|
29
|
+
return utils.reserve_port()
|
|
30
|
+
|
|
31
|
+
def release_port(self, number: int) -> None:
|
|
32
|
+
assert type(number) == int # noqa: E721
|
|
33
|
+
return utils.release_port(number)
|