Stop using unittest's addCleanup
See original GitHub issueWe want to stop using the unittest compatibility mode of pytest because:
- the current code does not work with pytest>=4.2 (possibly a bug in pytest)
- it’s better to use only one test framework, and pytest is nice 😃
The pure pytest migration has started in https://github.com/urllib3/urllib3/pull/1614 where @RatanShreshtha has worked on transforming all self.assert* calls into Python asserts. This was an important first part because that was the majority of unittest code.
Most of the remaining changes will now be quite limited in scope, but there’s one thing left that I would like to talk about here: the self.addCleanup calls. We have about 150 of them, and they ensure that the opened connections are closed. self.addCleanup is called on the following five classes:
- HTTPConnectionPool and subclass HTTPSConnectionPool
- PoolManager and subclass ProxyManager
- socks.SOCKSProxyManager
(Since this could be tackled by a contributor who isn’t familiar with context managers, I preferred to err on the side of verbosity here.)
Let’s look at an example:
def test_redirect(self):
http = PoolManager()
self.addCleanup(http.clear)
r = http.request("GET", "/redirect", ...)
assert r.status == 303
This is equivalent to:
def test_redirect(self):
http = PoolManager()
try:
r = http.request("GET", "/redirect", ...)
assert r.status == 303
finally:
http.clear()
If PoolManager had a close() method that behavec like clear(), this would also work:
import contextlib
def test_redirect(self):
with contextlib.closing(PoolManager()) as http:
r = http.request("GET", "/redirect", ...)
assert r.status == 303
Alternatively, if PoolManager implemented the context manager protocol (ie. had an __exit__ method), we could write:
def test_redirect(self):
with PoolManager() as http:
r = http.request("GET", "/redirect", ...)
assert r.status == 303
This last variant is slightly better than the addCleanup variant, I think. But can we do better? I think pytest fixtures can help here. For example, using a pool_manager fixture:
import pytest
@pytest.fixture
def pool_manager():
manager = PoolManager()
try:
yield manager
finally:
manager.clear()
def test_redirect(pool_manager):
r = http.request("GET", "/redirect", ...)
assert r.status == 303
This reduces verbosity in the tests themselves (look, only two lines left!). (I think that will also help with our async work because it will allow us to pass an AsyncPoolManager too, but that’s mostly hypothetical for now.)
Anyway, my proposal is to use the fixtures when there the classes are created without any parameters, and to use the with PoolManager(args, kwargs) as http: form otherwise.
What do you think of that plan, @sethmlarson?
Issue Analytics
- State:
- Created 4 years ago
- Comments:6 (6 by maintainers)

Top Related StackOverflow Question
I can open a PR for that.
You’re right, thanks!