safer 5.0.0__py3-none-any.whl → 5.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
safer/__init__.py
CHANGED
|
@@ -147,6 +147,8 @@ With `safer`
|
|
|
147
147
|
# Either the whole file is written, or nothing
|
|
148
148
|
|
|
149
149
|
"""
|
|
150
|
+
from __future__ import annotations
|
|
151
|
+
|
|
150
152
|
import contextlib
|
|
151
153
|
import functools
|
|
152
154
|
import io
|
|
@@ -162,15 +164,15 @@ __all__ = 'writer', 'open', 'closer', 'dump', 'printer'
|
|
|
162
164
|
|
|
163
165
|
|
|
164
166
|
def writer(
|
|
165
|
-
stream: t.
|
|
166
|
-
is_binary:
|
|
167
|
+
stream: t.Callable | None | t.IO | Path | str = None,
|
|
168
|
+
is_binary: bool | None = None,
|
|
167
169
|
close_on_exit: bool = False,
|
|
168
170
|
temp_file: bool = False,
|
|
169
171
|
chunk_size: int = 0x100000,
|
|
170
172
|
delete_failures: bool = True,
|
|
171
|
-
dry_run:
|
|
173
|
+
dry_run: bool | t.Callable = False,
|
|
172
174
|
enabled: bool = True,
|
|
173
|
-
) -> t.
|
|
175
|
+
) -> t.Callable | t.IO:
|
|
174
176
|
"""
|
|
175
177
|
Write safely to file streams, sockets and callables.
|
|
176
178
|
|
|
@@ -231,78 +233,84 @@ def writer(
|
|
|
231
233
|
if not enabled:
|
|
232
234
|
return stream
|
|
233
235
|
|
|
234
|
-
write: t.
|
|
236
|
+
write: t.Callable | None
|
|
235
237
|
|
|
236
|
-
if
|
|
237
|
-
|
|
238
|
+
if close_on_exit and stream in (sys.stdout, sys.stderr):
|
|
239
|
+
raise ValueError('You cannot close stdout or stderr')
|
|
238
240
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
+
if dry_run:
|
|
242
|
+
close_on_exit = False
|
|
241
243
|
|
|
242
|
-
|
|
243
|
-
if
|
|
244
|
-
|
|
244
|
+
try:
|
|
245
|
+
if callable(dry_run):
|
|
246
|
+
write, dry_run = dry_run, True
|
|
245
247
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
stream.write(v)
|
|
248
|
+
elif dry_run:
|
|
249
|
+
write = len
|
|
249
250
|
|
|
250
|
-
|
|
251
|
-
|
|
251
|
+
elif close_on_exit and hasattr(stream, 'write'):
|
|
252
|
+
if temp_file and BUG_MESSAGE:
|
|
253
|
+
raise NotImplementedError(BUG_MESSAGE)
|
|
252
254
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
+
def write(v):
|
|
256
|
+
with stream:
|
|
257
|
+
stream.write(v)
|
|
255
258
|
|
|
256
|
-
|
|
257
|
-
|
|
259
|
+
else:
|
|
260
|
+
write = getattr(stream, 'write', None)
|
|
258
261
|
|
|
259
|
-
|
|
260
|
-
|
|
262
|
+
send = getattr(stream, 'send', None)
|
|
263
|
+
mode = getattr(stream, 'mode', None)
|
|
261
264
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
+
if write and mode:
|
|
266
|
+
if not set('w+a').intersection(mode):
|
|
267
|
+
raise ValueError(f'Stream mode "{mode}" is not a write mode')
|
|
265
268
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
+
binary_mode = 'b' in mode
|
|
270
|
+
if is_binary is not None and is_binary is not binary_mode:
|
|
271
|
+
raise ValueError('is_binary is inconsistent with the file stream')
|
|
269
272
|
|
|
270
|
-
|
|
273
|
+
is_binary = binary_mode
|
|
271
274
|
|
|
272
|
-
|
|
273
|
-
|
|
275
|
+
elif dry_run:
|
|
276
|
+
pass
|
|
274
277
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
+
elif send and hasattr(stream, 'recv'): # It looks like a socket:
|
|
279
|
+
if not (is_binary is None or is_binary is True):
|
|
280
|
+
raise ValueError('is_binary=False is inconsistent with a socket')
|
|
278
281
|
|
|
279
|
-
|
|
280
|
-
|
|
282
|
+
write = send
|
|
283
|
+
is_binary = True
|
|
281
284
|
|
|
282
|
-
|
|
283
|
-
|
|
285
|
+
elif callable(stream):
|
|
286
|
+
write = stream
|
|
284
287
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
288
|
+
else:
|
|
289
|
+
raise ValueError('Stream is not a file, a socket, or callable')
|
|
290
|
+
|
|
291
|
+
closer: _StreamCloser
|
|
292
|
+
|
|
293
|
+
if temp_file:
|
|
294
|
+
closer = _FileStreamCloser(
|
|
295
|
+
write,
|
|
296
|
+
close_on_exit,
|
|
297
|
+
is_binary,
|
|
298
|
+
temp_file,
|
|
299
|
+
chunk_size,
|
|
300
|
+
delete_failures,
|
|
301
|
+
)
|
|
302
|
+
else:
|
|
303
|
+
closer = _MemoryStreamCloser(write, close_on_exit, is_binary)
|
|
304
|
+
|
|
305
|
+
if send is write:
|
|
306
|
+
closer.fp.send = write
|
|
301
307
|
|
|
302
|
-
|
|
303
|
-
closer.fp.send = write
|
|
308
|
+
return closer.fp
|
|
304
309
|
|
|
305
|
-
|
|
310
|
+
except Exception:
|
|
311
|
+
if close_on_exit:
|
|
312
|
+
getattr(stream, 'close', lambda: None)()
|
|
313
|
+
raise
|
|
306
314
|
|
|
307
315
|
|
|
308
316
|
# There's an edge case in #23 I can't yet fix, so I fail
|
|
@@ -311,18 +319,18 @@ BUG_MESSAGE = 'Sorry, safer.writer fails if temp_file (#23)'
|
|
|
311
319
|
|
|
312
320
|
|
|
313
321
|
def open(
|
|
314
|
-
name:
|
|
322
|
+
name: Path | str,
|
|
315
323
|
mode: str = 'r',
|
|
316
324
|
buffering: int = -1,
|
|
317
|
-
encoding:
|
|
318
|
-
errors:
|
|
319
|
-
newline:
|
|
325
|
+
encoding: str | None = None,
|
|
326
|
+
errors: str | None = None,
|
|
327
|
+
newline: str | None = None,
|
|
320
328
|
closefd: bool = True,
|
|
321
|
-
opener: t.
|
|
329
|
+
opener: t.Callable | None = None,
|
|
322
330
|
make_parents: bool = False,
|
|
323
331
|
delete_failures: bool = True,
|
|
324
332
|
temp_file: bool = False,
|
|
325
|
-
dry_run:
|
|
333
|
+
dry_run: bool | t.Callable = False,
|
|
326
334
|
enabled: bool = True,
|
|
327
335
|
) -> t.IO:
|
|
328
336
|
"""
|
|
@@ -385,7 +393,7 @@ def open(
|
|
|
385
393
|
name = str(name)
|
|
386
394
|
|
|
387
395
|
if not isinstance(name, str):
|
|
388
|
-
raise TypeError('`name` must be string, not
|
|
396
|
+
raise TypeError(f'`name` must be string, not {type(name).__name__}')
|
|
389
397
|
|
|
390
398
|
name = os.path.realpath(name)
|
|
391
399
|
parent = os.path.dirname(os.path.abspath(name))
|
|
@@ -431,7 +439,7 @@ def open(
|
|
|
431
439
|
raise ValueError("binary mode doesn't take an errors argument")
|
|
432
440
|
|
|
433
441
|
if 'x' in mode and os.path.exists(name):
|
|
434
|
-
raise FileExistsError("File exists: '
|
|
442
|
+
raise FileExistsError(f"File exists: '{name}'")
|
|
435
443
|
|
|
436
444
|
if buffering == -1:
|
|
437
445
|
buffering = io.DEFAULT_BUFFER_SIZE
|
|
@@ -447,8 +455,8 @@ def open(
|
|
|
447
455
|
|
|
448
456
|
|
|
449
457
|
def closer(
|
|
450
|
-
stream: t.IO, is_binary:
|
|
451
|
-
) -> t.
|
|
458
|
+
stream: t.IO, is_binary: bool | None = None, close_on_exit: bool = True, **kwds
|
|
459
|
+
) -> t.Callable | t.IO:
|
|
452
460
|
"""
|
|
453
461
|
Like `safer.writer()` but with `close_on_exit=True` by default
|
|
454
462
|
|
|
@@ -460,7 +468,7 @@ def closer(
|
|
|
460
468
|
|
|
461
469
|
def dump(
|
|
462
470
|
obj,
|
|
463
|
-
stream: t.
|
|
471
|
+
stream: t.Callable | None | t.IO | Path | str = None,
|
|
464
472
|
dump: t.Any = None,
|
|
465
473
|
**kwargs,
|
|
466
474
|
) -> t.Any:
|
|
@@ -533,8 +541,8 @@ def _get_dumper(dump: t.Any) -> t.Callable:
|
|
|
533
541
|
|
|
534
542
|
@contextlib.contextmanager
|
|
535
543
|
def printer(
|
|
536
|
-
name:
|
|
537
|
-
) -> t.
|
|
544
|
+
name: Path | str, mode: str = 'w', *args, **kwargs
|
|
545
|
+
) -> t.Iterator[t.Callable]:
|
|
538
546
|
"""
|
|
539
547
|
A context manager that yields a function that prints to the opened file,
|
|
540
548
|
only writing to the original file at the exit of the context,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
safer/__init__.py,sha256=gol1HEWHBCBlrotKRuS2mSln5GAyv6PWdML09x9GZI8,22692
|
|
2
|
+
safer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
safer-5.1.0.dist-info/LICENSE,sha256=YrPqlE_MughiZSHUT2iVoduqlqmStMop9EEwCTdlzBw,1067
|
|
4
|
+
safer-5.1.0.dist-info/METADATA,sha256=TiGbpkpfSotKcUdbSdeR8kz27YdpbD1EroL_D52Zi0M,5449
|
|
5
|
+
safer-5.1.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
6
|
+
safer-5.1.0.dist-info/RECORD,,
|
safer-5.0.0.dist-info/RECORD
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
safer/__init__.py,sha256=eRkfOSVTIr5Wd9aOF6EJBkfyizjeS_P6Uaeu0qBKe3Y,22455
|
|
2
|
-
safer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
safer-5.0.0.dist-info/LICENSE,sha256=YrPqlE_MughiZSHUT2iVoduqlqmStMop9EEwCTdlzBw,1067
|
|
4
|
-
safer-5.0.0.dist-info/METADATA,sha256=v2pjAfB1KHIGyg7W-fhSb0_mP2FOHePB8B7rPjzow8M,5449
|
|
5
|
-
safer-5.0.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
6
|
-
safer-5.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|