Add global TempDirectory manager

In cases where there is not a clear scope, or where enforcing a scope
and passing a temp directory to callees creates unnecessary coupling
between components, this will let us tie the lifetime of temporary
directories to the lifetime of the application without using e.g.
atexit or finalizers.

This has the benefit of being easier to test and reason about.
This commit is contained in:
Chris Hunt 2019-12-07 21:08:26 -05:00 committed by Christopher Hunt
parent cf743dd245
commit 0457826bd0
2 changed files with 45 additions and 3 deletions

View File

@ -8,17 +8,35 @@ import itertools
import logging
import os.path
import tempfile
from contextlib import contextmanager
from pip._vendor.contextlib2 import ExitStack
from pip._internal.utils.misc import rmtree
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
if MYPY_CHECK_RUNNING:
from typing import Optional
from typing import Iterator, Optional
logger = logging.getLogger(__name__)
_tempdir_manager = None # type: Optional[ExitStack]
@contextmanager
def global_tempdir_manager():
# type: () -> Iterator[None]
global _tempdir_manager
with ExitStack() as stack:
old_tempdir_manager, _tempdir_manager = _tempdir_manager, stack
try:
yield
finally:
_tempdir_manager = old_tempdir_manager
class TempDirectory(object):
"""Helper class that owns and cleans up a temporary directory.
@ -44,7 +62,8 @@ class TempDirectory(object):
self,
path=None, # type: Optional[str]
delete=None, # type: Optional[bool]
kind="temp"
kind="temp", # type: str
globally_managed=False, # type: bool
):
super(TempDirectory, self).__init__()
@ -61,6 +80,10 @@ class TempDirectory(object):
self.delete = delete
self.kind = kind
if globally_managed:
assert _tempdir_manager is not None
_tempdir_manager.enter_context(self)
@property
def path(self):
# type: () -> str

View File

@ -5,8 +5,13 @@ import tempfile
import pytest
from pip._internal.utils import temp_dir
from pip._internal.utils.misc import ensure_dir
from pip._internal.utils.temp_dir import AdjacentTempDirectory, TempDirectory
from pip._internal.utils.temp_dir import (
AdjacentTempDirectory,
TempDirectory,
global_tempdir_manager,
)
# No need to test symlinked directories on Windows
@ -188,3 +193,17 @@ def test_adjacent_directory_permission_error(monkeypatch):
with pytest.raises(OSError):
with AdjacentTempDirectory(original):
pass
def test_global_tempdir_manager():
with global_tempdir_manager():
d = TempDirectory(globally_managed=True)
path = d.path
assert os.path.exists(path)
assert not os.path.exists(path)
def test_tempdirectory_asserts_global_tempdir(monkeypatch):
monkeypatch.setattr(temp_dir, "_tempdir_manager", None)
with pytest.raises(AssertionError):
TempDirectory(globally_managed=True)