|  | 
| 12 | 12 | class PySysGetAttrTest(unittest.TestCase): | 
| 13 | 13 | 
 | 
| 14 | 14 |     common_faulthandler_code = textwrap.dedent(''' | 
| 15 |  | -        from contextlib import redirect_stderr | 
|  | 15 | +        import sys | 
| 16 | 16 |         from faulthandler import dump_traceback, enable, dump_traceback_later | 
| 17 |  | -        from threading import Thread | 
| 18 |  | -        import time | 
| 19 | 17 | 
 | 
| 20 |  | -        class FakeFile: | 
| 21 |  | -            def __init__(self): | 
| 22 |  | -                self.f = open("{0}", "w") | 
| 23 |  | -            def write(self, s): | 
| 24 |  | -                self.f.write(s) | 
|  | 18 | +        class FakeIO: | 
|  | 19 | +            def __init__(self, what): | 
|  | 20 | +                self.what = what | 
|  | 21 | +            def write(self, str): | 
|  | 22 | +                pass | 
| 25 | 23 |             def flush(self): | 
| 26 |  | -                time.sleep(0.2) | 
|  | 24 | +                pass | 
| 27 | 25 |             def fileno(self): | 
| 28 |  | -                time.sleep(0.2) | 
| 29 |  | -                return self.f.fileno() | 
|  | 26 | +                self.restore_std('stderr') | 
|  | 27 | +                return 0 | 
|  | 28 | +
 | 
|  | 29 | +            @staticmethod | 
|  | 30 | +            def restore_std(what): | 
|  | 31 | +                stdfile = getattr(sys, what) | 
|  | 32 | +                setattr(sys, what, getattr(sys, "__" + what + "__")) | 
|  | 33 | +                del stdfile | 
| 30 | 34 | 
 | 
| 31 |  | -        def thread1(): | 
| 32 |  | -            text = FakeFile() | 
| 33 |  | -            with redirect_stderr(text): | 
| 34 |  | -                time.sleep(0.2) | 
|  | 35 | +            @staticmethod | 
|  | 36 | +            def set_std(what): | 
|  | 37 | +                setattr(sys, what, FakeIO(what)) | 
| 35 | 38 | 
 | 
| 36 | 39 |         def main(): | 
| 37 | 40 |             enable(None, True) | 
| 38 |  | -            t1 = Thread(target=thread1, args=()) | 
| 39 |  | -            t1.start() | 
| 40 |  | -            time.sleep(0.1) | 
| 41 |  | -            {1} | 
|  | 41 | +            FakeIO.set_std('stderr') | 
|  | 42 | +            {0} | 
| 42 | 43 | 
 | 
| 43 | 44 |         if __name__ == "__main__": | 
| 44 | 45 |             main() | 
| 45 |  | -            exit(0) | 
| 46 | 46 |     ''') | 
| 47 | 47 | 
 | 
| 48 | 48 |     common_warnings_code = textwrap.dedent(''' | 
| @@ -210,150 +210,106 @@ def main(): | 
| 210 | 210 |             main() | 
| 211 | 211 |     ''') | 
| 212 | 212 | 
 | 
|  | 213 | +    print_code = textwrap.dedent(''' | 
|  | 214 | +        from io import StringIO | 
|  | 215 | +        import sys | 
| 213 | 216 | 
 | 
| 214 |  | -    def test_print_deleted_stdout(self): | 
| 215 |  | -        # print should use strong reference to the stdout. | 
| 216 |  | -        code = textwrap.dedent(''' | 
| 217 |  | -            from io import StringIO | 
| 218 |  | -            import sys | 
| 219 |  | -
 | 
| 220 |  | -            class Bar: | 
| 221 |  | -                def __init__(self): | 
| 222 |  | -                    self.x = sys.stdout | 
| 223 |  | -                    setattr(sys, "stdout", StringIO()) | 
| 224 |  | -
 | 
| 225 |  | -                def __repr__(self): | 
| 226 |  | -                    x = sys.stdout | 
| 227 |  | -                    setattr(sys, "stdout", self.x) | 
| 228 |  | -                    del x | 
| 229 |  | -                    return "Bar" | 
| 230 |  | -
 | 
| 231 |  | -            def main(): | 
| 232 |  | -                print(Bar()) | 
| 233 |  | -
 | 
| 234 |  | -            if __name__ == "__main__": | 
| 235 |  | -                main() | 
| 236 |  | -                exit(0) | 
| 237 |  | -        ''') | 
|  | 217 | +        class Bar: | 
|  | 218 | +            def __init__(self): | 
|  | 219 | +                self.x = sys.stdout | 
|  | 220 | +                setattr(sys, "stdout", StringIO()) | 
|  | 221 | +
 | 
|  | 222 | +            def __repr__(self): | 
|  | 223 | +                x = sys.stdout | 
|  | 224 | +                setattr(sys, "stdout", self.x) | 
|  | 225 | +                del x | 
|  | 226 | +                return "Bar" | 
|  | 227 | +
 | 
|  | 228 | +        def main(): | 
|  | 229 | +            print(Bar()) | 
|  | 230 | +
 | 
|  | 231 | +        if __name__ == "__main__": | 
|  | 232 | +            main() | 
|  | 233 | +            exit(0) | 
|  | 234 | +    ''') | 
|  | 235 | + | 
|  | 236 | +    def _check(self, code): | 
| 238 | 237 |         rc, out, err = assert_python_ok('-c', code) | 
| 239 | 238 |         self.assertEqual(rc, 0) | 
| 240 | 239 |         self.assertNotIn(b"Segmentation fault", err) | 
| 241 | 240 |         self.assertNotIn(b"access violation", err) | 
| 242 | 241 | 
 | 
|  | 242 | +    def test_print_deleted_stdout(self): | 
|  | 243 | +        # print should use strong reference to the stdout. | 
|  | 244 | +        self._check(self.print_code) | 
|  | 245 | + | 
| 243 | 246 |     def test_faulthandler_enable_deleted_stderr(self): | 
| 244 | 247 |         # faulthandler should use strong reference to the stderr | 
| 245 |  | -        with tempfile.TemporaryDirectory() as tmpdir: | 
| 246 |  | -            path = Path(tmpdir, "test_faulthandler_enable") | 
| 247 |  | -            test_code = self.common_faulthandler_code.format( | 
| 248 |  | -                path.as_posix(), | 
| 249 |  | -                "enable(None, True)" | 
| 250 |  | -            ) | 
| 251 |  | -            rc, out, err = assert_python_ok('-c', test_code) | 
| 252 |  | -            self.assertEqual(rc, 0) | 
| 253 |  | -            self.assertNotIn(b"Segmentation fault", err) | 
| 254 |  | -            self.assertNotIn(b"access violation", err) | 
|  | 248 | +        code = self.common_faulthandler_code.format( | 
|  | 249 | +            "enable(None, True)" | 
|  | 250 | +        ) | 
|  | 251 | +        self._check(code) | 
| 255 | 252 | 
 | 
| 256 | 253 |     def test_faulthandler_dump_traceback_deleted_stderr(self): | 
| 257 | 254 |         # faulthandler should use strong reference to the stderr | 
| 258 |  | -        with tempfile.TemporaryDirectory() as tmpdir: | 
| 259 |  | -            path = Path(tmpdir, "test_faulthandler_dump_traceback") | 
| 260 |  | -            test_code = self.common_faulthandler_code.format( | 
| 261 |  | -                path.as_posix(), | 
| 262 |  | -                "dump_traceback(None, False)" | 
| 263 |  | -            ) | 
| 264 |  | -            rc, out, err = assert_python_ok('-c', test_code) | 
| 265 |  | -            self.assertEqual(rc, 0) | 
| 266 |  | -            self.assertNotIn(b"Segmentation fault", err) | 
| 267 |  | -            self.assertNotIn(b"access violation", err) | 
|  | 255 | +        code = self.common_faulthandler_code.format( | 
|  | 256 | +            "dump_traceback(None, False)" | 
|  | 257 | +        ) | 
|  | 258 | +        self._check(code) | 
| 268 | 259 | 
 | 
| 269 | 260 |     def test_faulthandler_dump_traceback_later_deleted_stderr(self): | 
| 270 | 261 |         # faulthandler should use strong reference to the stderr | 
| 271 |  | -        with tempfile.TemporaryDirectory() as tmpdir: | 
| 272 |  | -            path = Path(tmpdir, "test_faulthandler_dump_traceback_later") | 
| 273 |  | -            test_code = self.common_faulthandler_code.format( | 
| 274 |  | -                path.as_posix(), | 
| 275 |  | -                "dump_traceback_later(0.1, True, None, False)" | 
| 276 |  | -            ) | 
| 277 |  | -            rc, out, err = assert_python_ok('-c', test_code) | 
| 278 |  | -            self.assertEqual(rc, 0) | 
| 279 |  | -            self.assertNotIn(b"Segmentation fault", err) | 
| 280 |  | -            self.assertNotIn(b"access violation", err) | 
|  | 262 | +        code = self.common_faulthandler_code.format( | 
|  | 263 | +            "dump_traceback_later(0.1, True, None, False)" | 
|  | 264 | +        ) | 
|  | 265 | +        self._check(code) | 
| 281 | 266 | 
 | 
| 282 | 267 |     def test_warnings_warn(self): | 
| 283 |  | -        test_code = self.common_warnings_code.format( | 
|  | 268 | +        code = self.common_warnings_code.format( | 
| 284 | 269 |             "warnings.warn(Foo())" | 
| 285 | 270 |         ) | 
| 286 |  | -        rc, _, err = assert_python_ok('-c', test_code) | 
| 287 |  | -        self.assertEqual(rc, 0) | 
| 288 |  | -        self.assertNotIn(b"Segmentation fault", err) | 
| 289 |  | -        self.assertNotIn(b"access violation", err) | 
|  | 271 | +        self._check(code) | 
| 290 | 272 | 
 | 
| 291 | 273 |     def test_warnings_warn_explicit(self): | 
| 292 |  | -        test_code = self.common_warnings_code.format( | 
|  | 274 | +        code = self.common_warnings_code.format( | 
| 293 | 275 |             "warnings.warn_explicit(Foo(), UserWarning, 'filename', 0)" | 
| 294 | 276 |         ) | 
| 295 |  | -        rc, _, err = assert_python_ok('-c', test_code) | 
| 296 |  | -        self.assertEqual(rc, 0) | 
| 297 |  | -        self.assertNotIn(b"Segmentation fault", err) | 
| 298 |  | -        self.assertNotIn(b"access violation", err) | 
|  | 277 | +        self._check(code) | 
| 299 | 278 | 
 | 
| 300 | 279 |     def test_input_stdin(self): | 
| 301 |  | -        test_code = self.common_input_code.format( | 
|  | 280 | +        code = self.common_input_code.format( | 
| 302 | 281 |             "", | 
| 303 | 282 |             "CrashStdin()" | 
| 304 | 283 |         ) | 
| 305 |  | -        rc, _, err = assert_python_ok('-c', test_code) | 
| 306 |  | -        self.assertEqual(rc, 0) | 
| 307 |  | -        self.assertNotIn(b"Segmentation fault", err) | 
| 308 |  | -        self.assertNotIn(b"access violation", err) | 
|  | 284 | +        self._check(code) | 
| 309 | 285 | 
 | 
| 310 | 286 |     def test_input_stdout(self): | 
| 311 |  | -        test_code = self.common_input_code.format( | 
|  | 287 | +        code = self.common_input_code.format( | 
| 312 | 288 |             "", | 
| 313 | 289 |             "CrashStdout()" | 
| 314 | 290 |         ) | 
| 315 |  | -        rc, _, err = assert_python_ok('-c', test_code) | 
| 316 |  | -        self.assertEqual(rc, 0) | 
| 317 |  | -        self.assertNotIn(b"Segmentation fault", err) | 
| 318 |  | -        self.assertNotIn(b"access violation", err) | 
|  | 291 | +        self._check(code) | 
| 319 | 292 | 
 | 
| 320 | 293 |     def test_input_stderr(self): | 
| 321 |  | -        test_code = self.common_input_code.format( | 
|  | 294 | +        code = self.common_input_code.format( | 
| 322 | 295 |             "sys.addaudithook(audit)", | 
| 323 | 296 |             "CrashStderr()" | 
| 324 | 297 |         ) | 
| 325 |  | -        rc, _, err = assert_python_ok('-c', test_code) | 
| 326 |  | -        self.assertEqual(rc, 0) | 
| 327 |  | -        self.assertNotIn(b"Segmentation fault", err) | 
| 328 |  | -        self.assertNotIn(b"access violation", err) | 
|  | 298 | +        self._check(code) | 
| 329 | 299 | 
 | 
| 330 | 300 |     def test_errors_unraisablehook(self): | 
| 331 |  | -        test_code = self.unraisable_hook_code | 
| 332 |  | -        rc, _, err = assert_python_ok('-c', test_code) | 
| 333 |  | -        self.assertEqual(rc, 0) | 
| 334 |  | -        self.assertNotIn(b"Segmentation fault", err) | 
| 335 |  | -        self.assertNotIn(b"access violation", err) | 
|  | 301 | +        self._check(self.unraisable_hook_code) | 
| 336 | 302 | 
 | 
| 337 | 303 |     def test_py_finalize_flush_std_files_stdout(self): | 
| 338 |  | -        test_code = self.flush_std_files_common_code.format("stdout") | 
| 339 |  | -        rc, _, err = assert_python_ok('-c', test_code) | 
| 340 |  | -        self.assertEqual(rc, 0) | 
| 341 |  | -        self.assertNotIn(b"Segmentation fault", err) | 
| 342 |  | -        self.assertNotIn(b"access violation", err) | 
|  | 304 | +        code = self.flush_std_files_common_code.format("stdout") | 
|  | 305 | +        self._check(code) | 
| 343 | 306 | 
 | 
| 344 | 307 |     def test_py_finalize_flush_std_files_stderr(self): | 
| 345 |  | -        test_code = self.flush_std_files_common_code.format("stderr") | 
| 346 |  | -        rc, _, err = assert_python_ok('-c', test_code) | 
| 347 |  | -        self.assertEqual(rc, 0) | 
| 348 |  | -        self.assertNotIn(b"Segmentation fault", err) | 
| 349 |  | -        self.assertNotIn(b"access violation", err) | 
|  | 308 | +        code = self.flush_std_files_common_code.format("stderr") | 
|  | 309 | +        self._check(code) | 
| 350 | 310 | 
 | 
| 351 | 311 |     def test_pyerr_printex_excepthook(self): | 
| 352 |  | -        test_code = self.pyerr_printex_code | 
| 353 |  | -        rc, _, err = assert_python_ok('-c', test_code) | 
| 354 |  | -        self.assertEqual(rc, 0) | 
| 355 |  | -        self.assertNotIn(b"Segmentation fault", err) | 
| 356 |  | -        self.assertNotIn(b"access violation", err) | 
|  | 312 | +        self._check(self.pyerr_printex_code) | 
| 357 | 313 | 
 | 
| 358 | 314 | if __name__ == "__main__": | 
| 359 | 315 |     unittest.main() | 
0 commit comments