| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent b7fb03a commit ca1a72b
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,3 +1,4 @@ | |||
| 1 | + using Python.Runtime; | ||
| 1 | 2 | using System; | |
| 2 | 3 | using System.Collections; | |
| 3 | 4 | using System.Collections.Generic; | |
@@ -81,6 +82,40 @@ public static void ThrowChainedExceptions() | |||
| 81 | 82 | throw new Exception("Outer exception", exc2); | |
| 82 | 83 | } | |
| 83 | 84 | } | |
| 85 | + | ||
| 86 | + public static IntPtr DoThrowSimple() | ||
| 87 | + { | ||
| 88 | + using (Py.GIL()) | ||
| 89 | + { | ||
| 90 | + dynamic builtins = Py.Import("builtins"); | ||
| 91 | + var typeErrorType = new PyType(builtins.TypeError); | ||
| 92 | + var pyerr = new PythonException(typeErrorType, value:null, traceback:null, "Type error, the first", innerException:null); | ||
| 93 | + throw new ArgumentException("Bogus bad parameter", pyerr); | ||
| 94 | + | ||
| 95 | + } | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + public static void DoThrowWithInner() | ||
| 99 | + { | ||
| 100 | + using(Py.GIL()) | ||
| 101 | + { | ||
| 102 | + // create a TypeError | ||
| 103 | + dynamic builtins = Py.Import("builtins"); | ||
| 104 | + var pyerrFirst = new PythonException(new PyType(builtins.TypeError), value:null, traceback:null, "Type error, the first", innerException:null); | ||
| 105 | + | ||
| 106 | + // Create an ArgumentException, but as a python exception, with the previous type error as the inner exception | ||
| 107 | + var argExc = new ArgumentException("Bogus bad parameter", pyerrFirst); | ||
| 108 | + var argExcPyObj = argExc.ToPython(); | ||
| 109 | + var pyArgExc = new PythonException(argExcPyObj.GetPythonType(), value:null, traceback:null, argExc.Message, innerException:argExc.InnerException); | ||
| 110 | + // This object must be disposed explicitly or else we get a false-positive leak. | ||
| 111 | + argExcPyObj.Dispose(); | ||
| 112 | + | ||
| 113 | + // Then throw a TypeError with the ArgumentException-as-python-error exception as inner. | ||
| 114 | + var pyerrSecond = new PythonException(new PyType(builtins.TypeError), value:null, traceback:null, "Type error, the second", innerException:pyArgExc); | ||
| 115 | + throw pyerrSecond; | ||
| 116 | + | ||
| 117 | + } | ||
| 118 | + } | ||
| 84 | 119 | } | |
| 85 | 120 | ||
| 86 | 121 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,6 +8,51 @@ | |||
| 8 | 8 | import pytest | |
| 9 | 9 | import pickle | |
| 10 | 10 | ||
| 11 | + # begin code from https://utcc.utoronto.ca/~cks/space/blog/python/GetAllObjects | ||
| 12 | + import gc | ||
| 13 | + # Recursively expand slist's objects | ||
| 14 | + # into olist, using seen to track | ||
| 15 | + # already processed objects. | ||
| 16 | + | ||
| 17 | + def _getr(slist, olist, seen): | ||
| 18 | + for e in slist: | ||
| 19 | + if id(e) in seen: | ||
| 20 | + continue | ||
| 21 | + seen[id(e)] = None | ||
| 22 | + olist.append(e) | ||
| 23 | + tl = gc.get_referents(e) | ||
| 24 | + if tl: | ||
| 25 | + _getr(tl, olist, seen) | ||
| 26 | + | ||
| 27 | + # The public function. | ||
| 28 | + def get_all_objects(): | ||
| 29 | + gcl = gc.get_objects() | ||
| 30 | + olist = [] | ||
| 31 | + seen = {} | ||
| 32 | + # Just in case: | ||
| 33 | + seen[id(gcl)] = None | ||
| 34 | + seen[id(olist)] = None | ||
| 35 | + seen[id(seen)] = None | ||
| 36 | + # _getr does the real work. | ||
| 37 | + _getr(gcl, olist, seen) | ||
| 38 | + return olist | ||
| 39 | + # end code from https://utcc.utoronto.ca/~cks/space/blog/python/GetAllObjects | ||
| 40 | + | ||
| 41 | + def leak_check(func): | ||
| 42 | + def do_leak_check(): | ||
| 43 | + func() | ||
| 44 | + gc.collect() | ||
| 45 | + exc = {x for x in get_all_objects() if isinstance(x, Exception) and not isinstance(x, pytest.PytestDeprecationWarning)} | ||
| 46 | + print(len(exc)) | ||
| 47 | + if len(exc): | ||
| 48 | + for x in exc: | ||
| 49 | + print('-------') | ||
| 50 | + print(repr(x)) | ||
| 51 | + print(gc.get_referrers(x)) | ||
| 52 | + print(len(gc.get_referrers(x))) | ||
| 53 | + assert False | ||
| 54 | + gc.collect() | ||
| 55 | + return do_leak_check | ||
| 11 | 56 | ||
| 12 | 57 | def test_unified_exception_semantics(): | |
| 13 | 58 | """Test unified exception semantics.""" | |
@@ -375,3 +420,33 @@ def test_iteration_innerexception(): | |||
| 375 | 420 | # after exception is thrown iterator is no longer valid | |
| 376 | 421 | with pytest.raises(StopIteration): | |
| 377 | 422 | next(val) | |
| 423 | + | ||
| 424 | + def leak_test(func): | ||
| 425 | + def do_test_leak(): | ||
| 426 | + # PyTest leaks things, gather the current state | ||
| 427 | + orig_exc = {x for x in get_all_objects() if isinstance(x, Exception)} | ||
| 428 | + func() | ||
| 429 | + exc = {x for x in get_all_objects() if isinstance(x, Exception)} | ||
| 430 | + possibly_leaked = exc - orig_exc | ||
| 431 | + assert not possibly_leaked | ||
| 432 | + | ||
| 433 | + return do_test_leak | ||
| 434 | + | ||
| 435 | + @leak_test | ||
| 436 | + def test_dont_leak_exceptions_simple(): | ||
| 437 | + from Python.Test import ExceptionTest | ||
| 438 | + | ||
| 439 | + try: | ||
| 440 | + ExceptionTest.DoThrowSimple() | ||
| 441 | + except System.ArgumentException: | ||
| 442 | + print('type error, as expected') | ||
| 443 | + | ||
| 444 | + @leak_test | ||
| 445 | + def test_dont_leak_exceptions_inner(): | ||
| 446 | + from Python.Test import ExceptionTest | ||
| 447 | + try: | ||
| 448 | + ExceptionTest.DoThrowWithInner() | ||
| 449 | + except TypeError: | ||
| 450 | + print('type error, as expected') | ||
| 451 | + except System.ArgumentException: | ||
| 452 | + print('type error, also expected') | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments