-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix memory leak with pytest.raises by using weakref #2006
Conversation
Thanks a lot for the PR! Here's a small test which fails on diff --git a/testing/python/raises.py b/testing/python/raises.py
index 59fd622..ba3eae2 100644
--- a/testing/python/raises.py
+++ b/testing/python/raises.py
@@ -96,3 +96,15 @@ class TestRaises:
assert e.msg == message
else:
assert False, "Expected pytest.raises.Exception"
+
+ def test_raises_leak(self):
+ import weakref
+
+ class T:
+ def __call__(self):
+ raise ValueError
+
+ t = T()
+ pytest.raises(ValueError, t)
+ t = weakref.ref(t)
+ assert t() is None On
|
@@ -9,6 +9,9 @@ | |||
* When loading plugins, import errors which contain non-ascii messages are now properly handled in Python 2 (`#1998`_). | |||
Thanks `@nicoddemus`_ for the PR. | |||
|
|||
* Fixed memory leak in the RaisesContext (pytest.raises) (`#1965`_). | |||
Thanks `@MSeifert04`_ for the report the PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to add links to the issue and your user name:
.. _@MSeifert04: https://github.com/MSeifert04
.. _#1965: https://github.com/pytest-dev/pytest/issues/1965
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also please add the test. 😄
Hmm better yet: diff --git a/testing/python/raises.py b/testing/python/raises.py
index 59fd622..f4222fc 100644
--- a/testing/python/raises.py
+++ b/testing/python/raises.py
@@ -96,3 +96,21 @@ class TestRaises:
assert e.msg == message
else:
assert False, "Expected pytest.raises.Exception"
+
+ @pytest.mark.parametrize('method', ['function', 'with'])
+ def test_raises_leak(self, method):
+ import weakref
+
+ class T:
+ def __call__(self):
+ raise ValueError
+
+ t = T()
+ if method == 'function':
+ pytest.raises(ValueError, t)
+ else:
+ with pytest.raises(ValueError):
+ t()
+
+ t = weakref.ref(t)
+ assert t() is None
This will test both forms of using |
@nicoddemus Thank you for the tests and how to fix the Changelog. I confirmed that the tests failed with master and passed with this branch, so I pushed another commit. |
I had no memoryleaks with this branch of pytest with python 2.7 using the But the |
Locally the I'm not sure about why is that, it should work... I will try to investigate this some more tomorrow, but if others have any ideas let's hear them. 😁 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gc.collect should fix the perceived issue
with pytest.raises(ValueError): | ||
t() | ||
|
||
t = weakref.ref(t) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this one might fail on pypy and other non-ref-counted python implementations
@RonnyPfannschmidt @nicoddemus As already pointed out the issue was non-existant because I forgot to call |
You mean calling I'm not sure, I like What do you think @RonnyPfannschmidt? |
@nicoddemus the test is incorrect correct, and also needs to call gc.collect |
Hmm perhaps. But AFAIU previously (2.9.2) there was no cycle at all and The fact that it fails only on py27 (requiring a |
I have to admit I'm totally confused now. 😕
Yes, it fixes the problem in my code, I haven't tested using |
We could skip that test on |
Using That seems to be some Python 2.7 contextmanager-weakref thingy because the following code returns a dead reference on Python 3.5 and an alive one on 2.7:
just to confirm this the same behaviour is also visible with
I could just skip the test for pypy and python 2.7 😄 |
Sorry. After it got complicated I somehow lost the motivation. The test doesn't work because of some (unknown) Python2, weakref, contextmanagers interaction. I wouldn't mind if any of the maintainers wants to fix it (I've set the checkbox to allow edits), or if I should just skip the |
@MSeifert04 no worries, we appreciate the effort you put into this. I plan to continue the work on this myself when I get the chance. |
In Python 2, a context manager's __exit__() leaves sys.exc_info with the exception values even when it was supposed to suppress the exception, so we explicitly call sys.exc_clear() which removes the traceback and allow the object to be released. Also updated the test to not depend on the immediate destruction of the object but instead to ensure it is not being tracked as a cyclic reference. Fix #1965
I found out the reason for the object to be kept alive in the test was that the traceback was still in Also updated the test to not depend on the immediate destruction of the object but instead to ensure it is not being tracked as a cyclic reference (I'm curious how that will fare on PyPy). @MSeifert04 I rebased the branch to resolve the conflicts, hope that's OK. |
@nicoddemus Nice detective work 👍 Thanks for rebase and the commit. You probably need to change the changelog to include yourself for the PR. If I remember correctly the tests were entirely your contribution and now also significant parts of the changes of the codebase. |
If someone else could review this in the next few days it would be great. I also plan to prepare |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well done, the final version looks good
Thanks @nicoddemus, @RonnyPfannschmidt 👍 |
@MSeifert04 thanks as well! |
Checklist:
AUTHORS
;CHANGELOG.rst
This fixes the memory leak locally (#1965) but I had a lot of problems when running the tests. I hope I don't waste too much of your CI ressources if I use the PR to run the tests.