19
19
20
20
if sys .platform == 'win32' :
21
21
22
- def _write_file (
23
- path : Path ,
24
- data : str | bytes ,
25
- * ,
26
- is_binary : bool ,
27
- ) -> str | None :
22
+ def _write_file (path : Path , data : str | bytes ) -> None :
28
23
"""Windows-specific file write implementation.
29
24
30
25
This implementation writes directly to the file without using a temporary file, because
31
26
they are problematic due to permissions issues on Windows.
32
27
"""
33
- if is_binary :
34
- path .write_bytes (data ) # type: ignore[arg-type]
28
+ if isinstance (data , bytes ):
29
+ path .write_bytes (data )
30
+ elif isinstance (data , str ):
31
+ path .write_text (data , encoding = 'utf-8' )
35
32
else :
36
- path .write_text (data , encoding = 'utf-8' ) # type: ignore[arg-type]
37
- return None
33
+ raise TypeError (f'Unsupported data type: { type (data )} . Expected str or bytes.' )
38
34
else :
39
35
40
- def _write_file (
41
- path : Path ,
42
- data : str | bytes ,
43
- * ,
44
- is_binary : bool ,
45
- ) -> str | None :
36
+ def _write_file (path : Path , data : str | bytes ) -> None :
46
37
"""Linux/Unix-specific file write implementation using temporary files."""
47
38
dir_path = path .parent
48
39
fd , tmp_path = tempfile .mkstemp (
@@ -51,17 +42,22 @@ def _write_file(
51
42
dir = str (dir_path ),
52
43
)
53
44
45
+ if not isinstance (data , (str , bytes )):
46
+ raise TypeError (f'Unsupported data type: { type (data )} . Expected str or bytes.' )
47
+
54
48
try :
55
- if is_binary :
49
+ if isinstance ( data , bytes ) :
56
50
with os .fdopen (fd , 'wb' ) as tmp_file :
57
- tmp_file .write (data ) # type: ignore[arg-type]
51
+ tmp_file .write (data )
58
52
else :
59
53
with os .fdopen (fd , 'w' , encoding = 'utf-8' ) as tmp_file :
60
- tmp_file .write (data ) # type: ignore[arg-type]
54
+ tmp_file .write (data )
55
+
56
+ # Atomically replace the destination file with the temporary file
57
+ Path (tmp_path ).replace (path )
61
58
except Exception :
62
59
Path (tmp_path ).unlink (missing_ok = True )
63
60
raise
64
- return tmp_path
65
61
66
62
67
63
def infer_mime_type (value : Any ) -> str :
@@ -106,7 +102,6 @@ async def atomic_write(
106
102
path : Path ,
107
103
data : str ,
108
104
* ,
109
- is_binary : bool = False ,
110
105
retry_count : int = 0 ,
111
106
) -> None : ...
112
107
@@ -116,7 +111,6 @@ async def atomic_write(
116
111
path : Path ,
117
112
data : bytes ,
118
113
* ,
119
- is_binary : bool = True ,
120
114
retry_count : int = 0 ,
121
115
) -> None : ...
122
116
@@ -125,48 +119,35 @@ async def atomic_write(
125
119
path : Path ,
126
120
data : str | bytes ,
127
121
* ,
128
- is_binary : bool = False ,
129
122
retry_count : int = 0 ,
130
123
) -> None :
131
124
"""Write data to a file atomically to prevent data corruption or partial writes.
132
125
133
- This function handles both text and binary data. It ensures atomic writing by creating
134
- a temporary file and then atomically replacing the target file, which prevents data
135
- corruption if the process is interrupted during the write operation.
126
+ This function handles both text and binary data. The binary mode is automatically
127
+ detected based on the data type (bytes = binary, str = text). It ensures atomic
128
+ writing by creating a temporary file and then atomically replacing the target file,
129
+ which prevents data corruption if the process is interrupted during the write operation.
136
130
137
131
Args:
138
132
path: The path to the destination file.
139
133
data: The data to write to the file (string or bytes).
140
- is_binary: If True, write in binary mode. If False (default), write in text mode.
141
134
retry_count: Internal parameter to track the number of retry attempts (default: 0).
142
135
"""
143
136
max_retries = 3
144
- tmp_path : str | None = None
145
137
146
138
try :
147
139
# Use the platform-specific write function resolved at import time.
148
- tmp_path = await asyncio .to_thread (_write_file , path , data , is_binary = is_binary )
149
-
150
- # On Linux/Unix, replace the destination file with tmp file.
151
- if tmp_path is not None :
152
- await asyncio .to_thread (os .replace , tmp_path , str (path ))
140
+ await asyncio .to_thread (_write_file , path , data )
153
141
except (FileNotFoundError , PermissionError ):
154
142
if retry_count < max_retries :
155
- if tmp_path is not None :
156
- await asyncio .to_thread (Path (tmp_path ).unlink , missing_ok = True )
157
143
return await atomic_write (
158
144
path ,
159
145
data ,
160
- is_binary = is_binary ,
161
146
retry_count = retry_count + 1 ,
162
147
)
163
148
# If we reach the maximum number of retries, raise the exception.
164
149
raise
165
150
166
- finally :
167
- if tmp_path is not None :
168
- await asyncio .to_thread (Path (tmp_path ).unlink , missing_ok = True )
169
-
170
151
171
152
async def export_json_to_stream (
172
153
iterator : AsyncIterator [dict [str , Any ]],
0 commit comments