Skip to content

Commit 9220dd2

Browse files
Add comments to tests
1 parent 4139409 commit 9220dd2

File tree

1 file changed

+44
-39
lines changed

1 file changed

+44
-39
lines changed

Lib/test/test_sys_getattr.py

Lines changed: 44 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ class PySysGetAttrTest(unittest.TestCase):
1515
import sys
1616
from faulthandler import dump_traceback, enable, dump_traceback_later
1717
18+
# The faulthandler_get_fileno calls stderr functions twice - the first
19+
# call is a fileno and the second one is a flush. So, if the stderr
20+
# changed while fileno is called, then we get a segfault when we
21+
# call flush after that.
1822
class FakeIO:
1923
def __init__(self, what):
2024
self.what = what
@@ -50,6 +54,12 @@ def main():
5054
import sys
5155
import warnings
5256
57+
# First of all, we have to delete _showwarnmsg to get into show_warning
58+
# function. The show_warning do series of calls of PyFile_WriteObject
59+
# and PyFile_WriteString functions. And one of the call is for __repr__
60+
# of warning's message. So if we change stderr while calling __repr__
61+
# (or concurently) we can get segfault from one of the consequence call
62+
# to write functions.
5363
class Foo:
5464
def __init__(self):
5565
self.x = sys.stderr
@@ -78,45 +88,28 @@ def write(self, str):
7888
pass
7989
def flush(self):
8090
pass
81-
def fileno(self):
82-
return 0
83-
84-
class CrashStdin:
85-
def __init__(self):
86-
self.stdin = sys.stdin
87-
setattr(sys, "stdin", FakeIO())
88-
89-
def __repr__(self):
90-
stdin = sys.stdin
91-
setattr(sys, "stdin", self.stdin)
92-
del stdin
93-
return "CrashStdin"
94-
95-
class CrashStdout:
96-
def __init__(self):
97-
self.stdout = sys.stdout
98-
setattr(sys, "stdout", FakeIO())
9991
100-
def __repr__(self):
101-
stdout = sys.stdout
102-
setattr(sys, "stdout", self.stdout)
103-
del stdout
104-
return "CrashStdout"
105-
106-
class CrashStderr:
107-
def __init__(self):
108-
self.stderr = sys.stderr
109-
setattr(sys, "stderr", FakeIO())
92+
# The input function gets borrowed refs for stdin, stdout and stderr.
93+
# As we use FakeIO without fileno the input functions thinks that we
94+
# are not tty and following happens:
95+
# audit is called, stderr is flushed, prompt's __repr__ is printed to
96+
# stdout and line is read from stdin. For stdin and stdout we can just
97+
# replace stdin and stdout from prompt's __repr__ and get segfault. But
98+
# for stderr we should do this from audit function.
99+
class CrashWhat:
100+
def __init__(self, what):
101+
self.what = what
102+
self.std = getattr(sys, what)
103+
setattr(sys, what, FakeIO())
110104
111105
def __repr__(self):
112-
stderr = sys.stderr
113-
setattr(sys, "stderr", self.stderr)
114-
del stderr
115-
return "CrashStderr"
106+
std = getattr(sys, self.what)
107+
setattr(sys, self.what, self.std)
108+
del std
109+
return "Crash"
116110
117111
def audit(event, args):
118-
if event == 'builtins.input':
119-
repr(args)
112+
repr(args)
120113
121114
def main():
122115
{0}
@@ -134,6 +127,10 @@ class UnraisableHookInitiator:
134127
def __del__(self):
135128
raise Exception('1')
136129
130+
# To get into unraisablehook we need to raise an exception from __del__
131+
# function. So, format_unraisable_v gets hook, calls audit
132+
# function and calls hook. If we revert back unraisablehook from audit
133+
# function we will get segfault when calling hook.
137134
class UnraisableHook:
138135
def __call__(self, *args, **kwds):
139136
print('X', *args)
@@ -161,15 +158,16 @@ def main():
161158
flush_std_files_common_code = textwrap.dedent('''
162159
import sys
163160
161+
# The flush_std_files function gets stdout and stderr. And then checks
162+
# if both of them are closed. And if so calls flush for them.
163+
# If we restore stdfile from FakeIO.closed property we can get segfault.
164164
class FakeIO:
165165
def __init__(self, what):
166166
self.what = what
167167
def write(self, str):
168168
pass
169169
def flush(self):
170170
pass
171-
def fileno(self):
172-
return 0
173171
174172
@property
175173
def closed(self):
@@ -188,6 +186,10 @@ def main():
188186
pyerr_printex_code = textwrap.dedent('''
189187
import sys
190188
189+
# To get into excepthook we should run invalid statement.
190+
# Then _PyErr_PrintEx gets excepthook, calls audit function and tries
191+
# to call hook. If we replace hook from audit (or concurently) we get
192+
# segfault.
191193
class Hook:
192194
def __call__(self, *args, **kwds):
193195
pass
@@ -214,6 +216,9 @@ def main():
214216
from io import StringIO
215217
import sys
216218
219+
# The print function gets stdout and does a series of calls write
220+
# functions. One of the function calls __repr__ and if we replace
221+
# stdout from __repr__ (or concurently) we get segfault.
217222
class Bar:
218223
def __init__(self):
219224
self.x = sys.stdout
@@ -279,21 +284,21 @@ def test_warnings_warn_explicit(self):
279284
def test_input_stdin(self):
280285
code = self.common_input_code.format(
281286
"",
282-
"CrashStdin()"
287+
"CrashWhat('stdin')"
283288
)
284289
self._check(code)
285290

286291
def test_input_stdout(self):
287292
code = self.common_input_code.format(
288293
"",
289-
"CrashStdout()"
294+
"CrashWhat('stdout')"
290295
)
291296
self._check(code)
292297

293298
def test_input_stderr(self):
294299
code = self.common_input_code.format(
295300
"sys.addaudithook(audit)",
296-
"CrashStderr()"
301+
"CrashWhat('stderr')"
297302
)
298303
self._check(code)
299304

0 commit comments

Comments
 (0)