Skip to content

Commit eebfdbf

Browse files
QueryException and StartNodeException are updated (sync with testgres.common 1.0.0) (#310)
Changes: 1) Testsgres uses testgres.os_ops v2.0.0 [master]. 2) QueryException - new: message and query are RO-properties - new: RO-property 'description' - message returns dynamically build text - new: method __repr__ - new: method __str__ is inhrited from TestgresException 3) StartNodeException - new: message and files are RO-properties - new: RO-property 'description' - message returns dynamically build text - new: method __repr__ - new: method __str__ is inhrited from TestgresException 4) Tests are added, too. 5) PostgresNode::_collect_special_files is updated - typing
1 parent 6f9c0ec commit eebfdbf

12 files changed

Lines changed: 229 additions & 23 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ dependencies = [
5454
"six>=1.9.0",
5555
"psutil",
5656
"packaging",
57-
"testgres.os_ops>=1.0.0,<2.0.0",
57+
"testgres.os_ops @ git+https://github.com/postgrespro/testgres.os_ops.git@2.0.0",
5858
]
5959

6060
[project.urls]

src/exceptions.py

Lines changed: 117 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# coding: utf-8
22

33
import six
4+
import typing
45

56
from testgres.operations.exceptions import TestgresException
67
from testgres.operations.exceptions import ExecUtilException
@@ -13,22 +14,71 @@ class PortForException(TestgresException):
1314

1415
@six.python_2_unicode_compatible
1516
class QueryException(TestgresException):
16-
def __init__(self, message=None, query=None):
17-
super(QueryException, self).__init__(message)
17+
_description: typing.Optional[str]
18+
_query: typing.Optional[str]
1819

19-
self.message = message
20-
self.query = query
20+
def __init__(
21+
self,
22+
message: typing.Optional[str] = None,
23+
query: typing.Optional[str] = None
24+
):
25+
assert message is None or type(message) == str # noqa: E721
26+
assert query is None or type(query) == str # noqa: E721
27+
28+
super().__init__(message)
29+
30+
self._description = message
31+
self._query = query
32+
return
33+
34+
@property
35+
def message(self) -> str:
36+
assert self._description is None or type(self._description) == str # noqa: E721
37+
assert self._query is None or type(self._query) == str # noqa: E721
2138

22-
def __str__(self):
2339
msg = []
2440

25-
if self.message:
26-
msg.append(self.message)
41+
if self._description:
42+
msg.append(self._description)
2743

28-
if self.query:
29-
msg.append(u'Query: {}'.format(self.query))
44+
if self._query:
45+
msg.append(u'Query: {}'.format(self._query))
3046

31-
return six.text_type('\n').join(msg)
47+
r = six.text_type('\n').join(msg)
48+
assert type(r) == str # noqa: E721
49+
return r
50+
51+
@property
52+
def description(self) -> typing.Optional[str]:
53+
assert self._description is None or type(self._description) == str # noqa: E721
54+
return self._description
55+
56+
@property
57+
def query(self) -> typing.Optional[str]:
58+
assert self._query is None or type(self._query) == str # noqa: E721
59+
return self._query
60+
61+
def __repr__(self) -> str:
62+
assert type(self) == QueryException # noqa: E721
63+
assert __class__ == QueryException # noqa: E721
64+
65+
args = []
66+
67+
if self._description is not None:
68+
args.append(("message", self._description))
69+
70+
if self._query is not None:
71+
args.append(("query", self._query))
72+
73+
result = "{}(".format(__class__.__name__)
74+
sep = ""
75+
for a in args:
76+
if a[1] is not None:
77+
result += sep + a[0] + "=" + repr(a[1])
78+
sep = ", "
79+
continue
80+
result += ")"
81+
return result
3282

3383

3484
class TimeoutException(QueryException):
@@ -41,23 +91,72 @@ class CatchUpException(QueryException):
4191

4292
@six.python_2_unicode_compatible
4393
class StartNodeException(TestgresException):
44-
def __init__(self, message=None, files=None):
45-
super(StartNodeException, self).__init__(message)
94+
_description: typing.Optional[str]
95+
_files: typing.Optional[typing.Iterable]
96+
97+
def __init__(
98+
self,
99+
message: typing.Optional[str] = None,
100+
files: typing.Optional[typing.Iterable] = None
101+
):
102+
assert message is None or type(message) == str # noqa: E721
103+
assert files is None or isinstance(files, typing.Iterable)
46104

47-
self.message = message
48-
self.files = files
105+
super().__init__(message)
106+
107+
self._description = message
108+
self._files = files
109+
return
110+
111+
@property
112+
def message(self) -> str:
113+
assert self._description is None or type(self._description) == str # noqa: E721
114+
assert self._files is None or isinstance(self._files, typing.Iterable)
49115

50-
def __str__(self):
51116
msg = []
52117

53-
if self.message:
54-
msg.append(self.message)
118+
if self._description:
119+
msg.append(self._description)
55120

56-
for f, lines in self.files or []:
121+
for f, lines in self._files or []:
122+
assert type(f) == str # noqa: E721
123+
assert type(lines) in [str, bytes] # noqa: E721
57124
msg.append(u'{}\n----\n{}\n'.format(f, lines))
58125

59126
return six.text_type('\n').join(msg)
60127

128+
@property
129+
def description(self) -> typing.Optional[str]:
130+
assert self._description is None or type(self._description) == str # noqa: E721
131+
return self._description
132+
133+
@property
134+
def files(self) -> typing.Optional[typing.Iterable]:
135+
assert self._files is None or isinstance(self._files, typing.Iterable)
136+
return self._files
137+
138+
def __repr__(self) -> str:
139+
assert type(self) == StartNodeException # noqa: E721
140+
assert __class__ == StartNodeException # noqa: E721
141+
142+
args = []
143+
144+
if self._description is not None:
145+
args.append(("message", self._description))
146+
147+
if self._files is not None:
148+
args.append(("files", self._files))
149+
150+
result = "{}(".format(__class__.__name__)
151+
sep = ""
152+
for a in args:
153+
if a[1] is not None:
154+
result += sep + a[0] + "=" + repr(a[1])
155+
sep = ", "
156+
continue
157+
result += ")"
158+
return result
159+
61160

62161
class InitNodeException(TestgresException):
63162
pass

src/node.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ def _maybe_stop_logger(self):
713713
if self._logger:
714714
self._logger.stop()
715715

716-
def _collect_special_files(self):
716+
def _collect_special_files(self) -> typing.List[typing.Tuple[str, bytes]]:
717717
result = []
718718

719719
# list of important files + last N lines

tests/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ pytest
33
pytest-xdist
44
psycopg2
55
six
6-
testgres.os_ops>=1.0.0,<2.0.0
6+
git+https://github.com/postgrespro/testgres.os_ops.git@2.0.0

tests/test_os_ops_remote.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ def test_rmdirs__try_to_delete_file(self, os_ops: OsOperations):
3333

3434
assert os.path.exists(path)
3535
assert type(x.value) == ExecUtilException # noqa: E721
36-
assert x.value.message == "Utility exited with non-zero code (20). Error: `cannot remove '" + path + "': it is not a directory`"
36+
assert x.value.description == "Utility exited with non-zero code (20). Error: `cannot remove '" + path + "': it is not a directory`"
37+
assert x.value.message.startswith(x.value.description)
3738
assert type(x.value.error) == str # noqa: E721
3839
assert x.value.error.strip() == "cannot remove '" + path + "': it is not a directory"
3940
assert type(x.value.exit_code) == int # noqa: E721

tests/test_testgres_common.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,11 @@ def test_double_start(self, node_svc: PostgresNodeService):
219219

220220
assert x is not None
221221
assert type(x.value) == StartNodeException # noqa: E721
222+
assert type(x.value.description) == str # noqa: E721
222223
assert type(x.value.message) == str # noqa: E721
223224

224-
assert x.value.message == "Cannot start node"
225+
assert x.value.description == "Cannot start node"
226+
assert x.value.message.startswith(x.value.description)
225227

226228
assert node.is_started
227229

tests/units/__init__.py

Whitespace-only changes.

tests/units/exceptions/QueryException/__init__.py

Whitespace-only changes.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from src.exceptions import QueryException
2+
from src.exceptions import TestgresException as testgres__TestgresException
3+
4+
5+
class TestSet001_Constructor:
6+
def test_001__default(self):
7+
e = QueryException()
8+
assert type(e) == QueryException # noqa: E721
9+
assert isinstance(e, testgres__TestgresException)
10+
assert e.source is None
11+
assert e.message == ""
12+
assert e.description is None
13+
assert e.query is None
14+
assert str(e) == ""
15+
assert repr(e) == "QueryException()"
16+
return
17+
18+
def test_002__message(self):
19+
e = QueryException(message="abc\n123")
20+
assert type(e) == QueryException # noqa: E721
21+
assert isinstance(e, testgres__TestgresException)
22+
assert e.source is None
23+
assert e.message == "abc\n123"
24+
assert e.description == "abc\n123"
25+
assert e.query is None
26+
assert str(e) == "abc\n123"
27+
assert repr(e) == "QueryException(message='abc\\n123')"
28+
return
29+
30+
def test_003__query(self):
31+
e = QueryException(query="cba\n321")
32+
assert type(e) == QueryException # noqa: E721
33+
assert isinstance(e, testgres__TestgresException)
34+
assert e.source is None
35+
assert e.message == "Query: cba\n321"
36+
assert e.description is None
37+
assert e.query == "cba\n321"
38+
assert str(e) == "Query: cba\n321"
39+
assert repr(e) == "QueryException(query='cba\\n321')"
40+
return
41+
42+
def test_004__all(self):
43+
e = QueryException(message="mmm", query="cba\n321")
44+
assert type(e) == QueryException # noqa: E721
45+
assert isinstance(e, testgres__TestgresException)
46+
assert e.source is None
47+
assert e.message == "mmm\nQuery: cba\n321"
48+
assert e.description == "mmm"
49+
assert e.query == "cba\n321"
50+
assert str(e) == "mmm\nQuery: cba\n321"
51+
assert repr(e) == "QueryException(message='mmm', query='cba\\n321')"
52+
return

tests/units/exceptions/StartNodeException/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)