7
7
import json
8
8
import os
9
9
from pathlib import Path
10
+ import tempfile
10
11
from typing import Dict
11
12
from typing import final
12
13
from typing import Generator
@@ -123,6 +124,10 @@ def warn(self, fmt: str, *, _ispytest: bool = False, **args: object) -> None:
123
124
stacklevel = 3 ,
124
125
)
125
126
127
+ def _mkdir (self , path : Path ) -> None :
128
+ self ._ensure_cache_dir_and_supporting_files ()
129
+ path .mkdir (exist_ok = True , parents = True )
130
+
126
131
def mkdir (self , name : str ) -> Path :
127
132
"""Return a directory path object with the given name.
128
133
@@ -141,7 +146,7 @@ def mkdir(self, name: str) -> Path:
141
146
if len (path .parts ) > 1 :
142
147
raise ValueError ("name is not allowed to contain path separators" )
143
148
res = self ._cachedir .joinpath (self ._CACHE_PREFIX_DIRS , path )
144
- res . mkdir ( exist_ok = True , parents = True )
149
+ self . _mkdir ( res )
145
150
return res
146
151
147
152
def _getvaluepath (self , key : str ) -> Path :
@@ -178,19 +183,13 @@ def set(self, key: str, value: object) -> None:
178
183
"""
179
184
path = self ._getvaluepath (key )
180
185
try :
181
- if path .parent .is_dir ():
182
- cache_dir_exists_already = True
183
- else :
184
- cache_dir_exists_already = self ._cachedir .exists ()
185
- path .parent .mkdir (exist_ok = True , parents = True )
186
+ self ._mkdir (path .parent )
186
187
except OSError as exc :
187
188
self .warn (
188
189
f"could not create cache path { path } : { exc } " ,
189
190
_ispytest = True ,
190
191
)
191
192
return
192
- if not cache_dir_exists_already :
193
- self ._ensure_supporting_files ()
194
193
data = json .dumps (value , ensure_ascii = False , indent = 2 )
195
194
try :
196
195
f = path .open ("w" , encoding = "UTF-8" )
@@ -203,17 +202,32 @@ def set(self, key: str, value: object) -> None:
203
202
with f :
204
203
f .write (data )
205
204
206
- def _ensure_supporting_files (self ) -> None :
207
- """Create supporting files in the cache dir that are not really part of the cache."""
208
- readme_path = self ._cachedir / "README.md"
209
- readme_path .write_text (README_CONTENT , encoding = "UTF-8" )
210
-
211
- gitignore_path = self ._cachedir .joinpath (".gitignore" )
212
- msg = "# Created by pytest automatically.\n *\n "
213
- gitignore_path .write_text (msg , encoding = "UTF-8" )
205
+ def _ensure_cache_dir_and_supporting_files (self ) -> None :
206
+ """Create the cache dir and its supporting files."""
207
+ if self ._cachedir .is_dir ():
208
+ return
214
209
215
- cachedir_tag_path = self ._cachedir .joinpath ("CACHEDIR.TAG" )
216
- cachedir_tag_path .write_bytes (CACHEDIR_TAG_CONTENT )
210
+ self ._cachedir .parent .mkdir (parents = True , exist_ok = True )
211
+ with tempfile .TemporaryDirectory (
212
+ prefix = "pytest-cache-files-" ,
213
+ dir = self ._cachedir .parent ,
214
+ ) as newpath :
215
+ path = Path (newpath )
216
+ with open (path .joinpath ("README.md" ), "xt" , encoding = "UTF-8" ) as f :
217
+ f .write (README_CONTENT )
218
+ with open (path .joinpath (".gitignore" ), "xt" , encoding = "UTF-8" ) as f :
219
+ f .write ("# Created by pytest automatically.\n *\n " )
220
+ with open (path .joinpath ("CACHEDIR.TAG" ), "xb" ) as f :
221
+ f .write (CACHEDIR_TAG_CONTENT )
222
+
223
+ path .rename (self ._cachedir )
224
+ # Create a directory in place of the one we just moved so that `TemporaryDirectory`'s
225
+ # cleanup doesn't complain.
226
+ #
227
+ # TODO: pass ignore_cleanup_errors=True when we no longer support python < 3.10. See
228
+ # https://github.com/python/cpython/issues/74168. Note that passing delete=False would
229
+ # do the wrong thing in case of errors and isn't supported until python 3.12.
230
+ path .mkdir ()
217
231
218
232
219
233
class LFPluginCollWrapper :
0 commit comments