pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/pythonnet/pythonnet/commit/ca1a72b1bd7ed9771189cf3dd99322bcb9420e81

60fa.css" /> Add tests for exception leaking. · pythonnet/pythonnet@ca1a72b · GitHub
Skip to content

Commit ca1a72b

Browse files
BadSingletonlostmsu
authored andcommitted
Add tests for exception leaking.
Originally from PR #1402. The underlying bug is now fixed, but the tests are atill applicable.
1 parent b7fb03a commit ca1a72b

2 files changed

Lines changed: 110 additions & 0 deletions

File tree

src/testing/exceptiontest.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using Python.Runtime;
12
using System;
23
using System.Collections;
34
using System.Collections.Generic;
@@ -81,6 +82,40 @@ public static void ThrowChainedExceptions()
8182
throw new Exception("Outer exception", exc2);
8283
}
8384
}
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+
}
84119
}
85120

86121

tests/test_exceptions.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,51 @@
88
import pytest
99
import pickle
1010

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
1156

1257
def test_unified_exception_semantics():
1358
"""Test unified exception semantics."""
@@ -375,3 +420,33 @@ def test_iteration_innerexception():
375420
# after exception is thrown iterator is no longer valid
376421
with pytest.raises(StopIteration):
377422
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')

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy