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