Skip to content

Commit 87f4bff

Browse files
authored
Merge pull request #7652 from chrahunt/refactor/configure-tempdir-registry-in-command
Configure tempdir registry in BaseCommand
2 parents ace7d09 + f2af7df commit 87f4bff

File tree

5 files changed

+130
-12
lines changed

5 files changed

+130
-12
lines changed

src/pip/_internal/cli/base_command.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,21 @@
3434
from pip._internal.utils.filesystem import check_path_owner
3535
from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging
3636
from pip._internal.utils.misc import get_prog, normalize_path
37-
from pip._internal.utils.temp_dir import global_tempdir_manager
37+
from pip._internal.utils.temp_dir import (
38+
global_tempdir_manager,
39+
tempdir_registry,
40+
)
3841
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
3942
from pip._internal.utils.virtualenv import running_under_virtualenv
4043

4144
if MYPY_CHECK_RUNNING:
42-
from typing import List, Tuple, Any
45+
from typing import List, Optional, Tuple, Any
4346
from optparse import Values
4447

48+
from pip._internal.utils.temp_dir import (
49+
TempDirectoryTypeRegistry as TempDirRegistry
50+
)
51+
4552
__all__ = ['Command']
4653

4754
logger = logging.getLogger(__name__)
@@ -68,6 +75,8 @@ def __init__(self, name, summary, isolated=False):
6875
self.summary = summary
6976
self.parser = ConfigOptionParser(**parser_kw)
7077

78+
self.tempdir_registry = None # type: Optional[TempDirRegistry]
79+
7180
# Commands should add options to this option group
7281
optgroup_name = '%s Options' % self.name.capitalize()
7382
self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name)
@@ -108,6 +117,10 @@ def main(self, args):
108117

109118
def _main(self, args):
110119
# type: (List[str]) -> int
120+
# We must initialize this before the tempdir manager, otherwise the
121+
# configuration would not be accessible by the time we clean up the
122+
# tempdir manager.
123+
self.tempdir_registry = self.enter_context(tempdir_registry())
111124
# Intentionally set as early as possible so globally-managed temporary
112125
# directories are available to the rest of the code.
113126
self.enter_context(global_tempdir_manager())

src/pip/_internal/utils/temp_dir.py

+18-6
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
1414

1515
if MYPY_CHECK_RUNNING:
16-
from typing import Any, Dict, Iterator, Optional, TypeVar
16+
from typing import Any, Dict, Iterator, Optional, TypeVar, Union
1717

1818
_T = TypeVar('_T', bound='TempDirectory')
1919

@@ -77,6 +77,13 @@ def tempdir_registry():
7777
_tempdir_registry = old_tempdir_registry
7878

7979

80+
class _Default(object):
81+
pass
82+
83+
84+
_default = _Default()
85+
86+
8087
class TempDirectory(object):
8188
"""Helper class that owns and cleans up a temporary directory.
8289
@@ -101,16 +108,21 @@ class TempDirectory(object):
101108
def __init__(
102109
self,
103110
path=None, # type: Optional[str]
104-
delete=None, # type: Optional[bool]
111+
delete=_default, # type: Union[bool, None, _Default]
105112
kind="temp", # type: str
106113
globally_managed=False, # type: bool
107114
):
108115
super(TempDirectory, self).__init__()
109116

110-
# If we were given an explicit directory, resolve delete option now.
111-
# Otherwise we wait until cleanup and see what tempdir_registry says.
112-
if path is not None and delete is None:
113-
delete = False
117+
if delete is _default:
118+
if path is not None:
119+
# If we were given an explicit directory, resolve delete option
120+
# now.
121+
delete = False
122+
else:
123+
# Otherwise, we wait until cleanup and see what
124+
# tempdir_registry says.
125+
delete = None
114126

115127
if path is None:
116128
path = self._create(kind)

tests/conftest.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
import pytest
1212
import six
13-
from pip._vendor.contextlib2 import ExitStack
13+
from pip._vendor.contextlib2 import ExitStack, nullcontext
1414
from setuptools.wheel import Wheel
1515

1616
from pip._internal.cli.main import main as pip_entry_point
@@ -188,13 +188,18 @@ def isolate(tmpdir):
188188

189189

190190
@pytest.fixture(autouse=True)
191-
def scoped_global_tempdir_manager():
191+
def scoped_global_tempdir_manager(request):
192192
"""Make unit tests with globally-managed tempdirs easier
193193
194194
Each test function gets its own individual scope for globally-managed
195195
temporary directories in the application.
196196
"""
197-
with global_tempdir_manager():
197+
if "no_auto_tempdir_manager" in request.keywords:
198+
ctx = nullcontext
199+
else:
200+
ctx = global_tempdir_manager
201+
202+
with ctx():
198203
yield
199204

200205

tests/unit/test_base_command.py

+67-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
import os
33
import time
44

5-
from mock import patch
5+
import pytest
6+
from mock import Mock, patch
67

78
from pip._internal.cli.base_command import Command
9+
from pip._internal.cli.status_codes import SUCCESS
10+
from pip._internal.utils import temp_dir
811
from pip._internal.utils.logging import BrokenStdoutLoggingError
12+
from pip._internal.utils.temp_dir import TempDirectory
913

1014

1115
class FakeCommand(Command):
@@ -145,3 +149,65 @@ def test_unicode_messages(self, tmpdir):
145149
cmd = FakeCommandWithUnicode()
146150
log_path = tmpdir.joinpath('log')
147151
cmd.main(['fake_unicode', '--log', log_path])
152+
153+
154+
@pytest.mark.no_auto_tempdir_manager
155+
def test_base_command_provides_tempdir_helpers():
156+
assert temp_dir._tempdir_manager is None
157+
assert temp_dir._tempdir_registry is None
158+
159+
def assert_helpers_set(options, args):
160+
assert temp_dir._tempdir_manager is not None
161+
assert temp_dir._tempdir_registry is not None
162+
163+
c = Command("fake", "fake")
164+
c.run = Mock(side_effect=assert_helpers_set)
165+
assert c.main(["fake"]) == SUCCESS
166+
c.run.assert_called_once()
167+
168+
169+
not_deleted = "not_deleted"
170+
171+
172+
@pytest.mark.parametrize("kind,exists", [
173+
(not_deleted, True), ("deleted", False)
174+
])
175+
@pytest.mark.no_auto_tempdir_manager
176+
def test_base_command_global_tempdir_cleanup(kind, exists):
177+
assert temp_dir._tempdir_manager is None
178+
assert temp_dir._tempdir_registry is None
179+
180+
class Holder(object):
181+
value = None
182+
183+
def create_temp_dirs(options, args):
184+
c.tempdir_registry.set_delete(not_deleted, False)
185+
Holder.value = TempDirectory(kind=kind, globally_managed=True).path
186+
187+
c = Command("fake", "fake")
188+
c.run = Mock(side_effect=create_temp_dirs)
189+
assert c.main(["fake"]) == SUCCESS
190+
c.run.assert_called_once()
191+
assert os.path.exists(Holder.value) == exists
192+
193+
194+
@pytest.mark.parametrize("kind,exists", [
195+
(not_deleted, True), ("deleted", False)
196+
])
197+
@pytest.mark.no_auto_tempdir_manager
198+
def test_base_command_local_tempdir_cleanup(kind, exists):
199+
assert temp_dir._tempdir_manager is None
200+
assert temp_dir._tempdir_registry is None
201+
202+
def create_temp_dirs(options, args):
203+
c.tempdir_registry.set_delete(not_deleted, False)
204+
205+
with TempDirectory(kind=kind) as d:
206+
path = d.path
207+
assert os.path.exists(path)
208+
assert os.path.exists(path) == exists
209+
210+
c = Command("fake", "fake")
211+
c.run = Mock(side_effect=create_temp_dirs)
212+
assert c.main(["fake"]) == SUCCESS
213+
c.run.assert_called_once()

tests/unit/test_utils_temp_dir.py

+22
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from pip._internal.utils.temp_dir import (
1111
AdjacentTempDirectory,
1212
TempDirectory,
13+
_default,
1314
global_tempdir_manager,
1415
tempdir_registry,
1516
)
@@ -216,12 +217,15 @@ def test_tempdirectory_asserts_global_tempdir(monkeypatch):
216217

217218
@pytest.mark.parametrize("delete,kind,exists", [
218219
(None, deleted_kind, False),
220+
(_default, deleted_kind, False),
219221
(True, deleted_kind, False),
220222
(False, deleted_kind, True),
221223
(None, not_deleted_kind, True),
224+
(_default, not_deleted_kind, True),
222225
(True, not_deleted_kind, False),
223226
(False, not_deleted_kind, True),
224227
(None, "unspecified", False),
228+
(_default, "unspecified", False),
225229
(True, "unspecified", False),
226230
(False, "unspecified", True),
227231
])
@@ -236,6 +240,24 @@ def test_tempdir_registry(kind, delete, exists):
236240
assert os.path.exists(path) == exists
237241

238242

243+
@pytest.mark.parametrize("delete,exists", [
244+
(_default, True), (None, False)
245+
])
246+
def test_temp_dir_does_not_delete_explicit_paths_by_default(
247+
tmpdir, delete, exists
248+
):
249+
path = tmpdir / "example"
250+
path.mkdir()
251+
252+
with tempdir_registry() as registry:
253+
registry.set_delete(deleted_kind, True)
254+
255+
with TempDirectory(path=path, delete=delete, kind=deleted_kind) as d:
256+
assert str(d.path) == path
257+
assert os.path.exists(path)
258+
assert os.path.exists(path) == exists
259+
260+
239261
@pytest.mark.parametrize("should_delete", [True, False])
240262
def test_tempdir_registry_lazy(should_delete):
241263
"""

0 commit comments

Comments
 (0)