Skip to content

Commit 0a570cf

Browse files
authored
Add files via upload
1 parent 21d9f14 commit 0a570cf

1 file changed

Lines changed: 94 additions & 38 deletions

File tree

pyarchivefile.py

Lines changed: 94 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,31 +2059,48 @@ def MkTempFile(data=None,
20592059
prefix=__program_name__,
20602060
delete=True,
20612061
encoding="utf-8",
2062-
newline=None, # text mode only; in-memory objects ignore newline semantics
2062+
newline=None,
2063+
text_errors="strict",
20632064
dir=None,
20642065
suffix="",
20652066
use_spool=__use_spoolfile__,
2067+
autoswitch_spool=False,
20662068
spool_max=__spoolfile_size__,
2067-
spool_dir=__use_spooldir__):
2069+
spool_dir=__use_spooldir__,
2070+
reset_to_start=True,
2071+
memfd_name=None,
2072+
memfd_allow_sealing=False,
2073+
memfd_flags_extra=0,
2074+
on_create=None):
20682075
"""
20692076
Return a file-like handle with consistent behavior on Py2.7 and Py3.x.
20702077

20712078
Storage:
2072-
- inmem=True -> BytesIO (bytes) or StringIO (text), or memfd for bytes if available
2073-
- inmem=False, use_spool=True -> SpooledTemporaryFile (binary), optionally TextIOWrapper for text
2074-
- inmem=False, use_spool=False -> NamedTemporaryFile (binary), optionally TextIOWrapper for text
2079+
- inmem=True, usememfd=True, isbytes=True and memfd available
2080+
-> memfd-backed anonymous file (binary)
2081+
- inmem=True, otherwise
2082+
-> BytesIO (bytes) or StringIO (text)
2083+
- inmem=False, use_spool=True
2084+
-> SpooledTemporaryFile (binary), optionally TextIOWrapper for text
2085+
- inmem=False, use_spool=False
2086+
-> NamedTemporaryFile (binary), optionally TextIOWrapper for text
20752087

20762088
Text vs bytes:
20772089
- isbytes=True -> file expects bytes; 'data' must be bytes-like
2078-
- isbytes=False -> file expects text; 'data' must be text (unicode/str). Newline translation and encoding
2079-
apply only for spooled/named files (not BytesIO/StringIO).
2090+
- isbytes=False -> file expects text; 'data' must be text (unicode/str). Newline translation and
2091+
encoding apply only for spooled/named files (not BytesIO/StringIO).
20802092

20812093
Notes:
2082-
- On Windows, NamedTemporaryFile(delete=True) keeps the file open and cannot be reopened by other processes.
2083-
Use delete=False if you need to pass the path elsewhere.
2084-
- For text: in-memory StringIO ignores 'newline' (as usual).
2085-
- When available, memfd is used only for inmem=True and isbytes=True, providing an anonymous in-memory
2086-
file descriptor (Linux-only). Text in-memory still uses StringIO to preserve newline semantics.
2094+
- On Windows, NamedTemporaryFile(delete=True) keeps the file open and cannot be reopened by
2095+
other processes. Use delete=False if you need to pass the path elsewhere.
2096+
- For text: in-memory StringIO ignores 'newline' and 'text_errors' (as usual).
2097+
- When available, and if usememfd=True, memfd is used only for inmem=True and isbytes=True,
2098+
providing an anonymous in-memory file descriptor (Linux-only). Text in-memory still uses
2099+
StringIO to preserve newline semantics.
2100+
- If autoswitch_spool=True and initial data size exceeds spool_max, in-memory storage is
2101+
skipped and a spooled file is used instead (if use_spool=True).
2102+
- If on_create is not None, it is called as on_create(fp, kind) where kind is one of:
2103+
"memfd", "bytesio", "stringio", "spool", "disk".
20872104
"""
20882105

20892106
# -- sanitize simple params (avoid None surprises) --
@@ -2115,39 +2132,65 @@ def MkTempFile(data=None,
21152132
else:
21162133
init = None
21172134

2135+
# Size of init for autoswitch; only meaningful for bytes
2136+
init_len = len(init) if (init is not None and isbytes) else None
2137+
21182138
# -------- In-memory --------
21192139
if inmem:
2120-
# Use memfd only for bytes, and only where available (Linux, Python 3.8+)
2121-
if usememfd and isbytes and hasattr(os, "memfd_create"):
2122-
flags = 0
2123-
# Close-on-exec is almost always what you want for temps
2124-
if hasattr(os, "MFD_CLOEXEC"):
2125-
flags |= os.MFD_CLOEXEC
2126-
2127-
fd = os.memfd_create(prefix, flags)
2128-
# Binary read/write file-like object backed by RAM
2129-
f = os.fdopen(fd, "w+b")
2130-
2131-
if init is not None:
2132-
f.write(init)
2133-
f.seek(0)
2134-
return f
2140+
# If autoswitch is enabled and data is larger than spool_max, and
2141+
# spooling is allowed, skip the in-memory branch and fall through
2142+
# to the spool/disk logic below.
2143+
if autoswitch_spool and use_spool and init_len is not None and init_len > spool_max:
2144+
pass # fall through to spool/disk sections
2145+
else:
2146+
# Use memfd only for bytes, and only where available (Linux, Python 3.8+)
2147+
if usememfd and isbytes and hasattr(os, "memfd_create"):
2148+
name = memfd_name or prefix or "MkTempFile"
2149+
flags = 0
2150+
# Close-on-exec is almost always what you want for temps
2151+
if hasattr(os, "MFD_CLOEXEC"):
2152+
flags |= os.MFD_CLOEXEC
2153+
# Optional sealing support if requested and available
2154+
if memfd_allow_sealing and hasattr(os, "MFD_ALLOW_SEALING"):
2155+
flags |= os.MFD_ALLOW_SEALING
2156+
# Extra custom flags (e.g. hugepage flags) if caller wants them
2157+
if memfd_flags_extra:
2158+
flags |= memfd_flags_extra
2159+
2160+
fd = os.memfd_create(name, flags)
2161+
# Binary read/write file-like object backed by RAM
2162+
f = os.fdopen(fd, "w+b")
2163+
2164+
if init is not None:
2165+
f.write(init)
2166+
if reset_to_start:
2167+
f.seek(0)
2168+
2169+
if on_create is not None:
2170+
on_create(f, "memfd")
2171+
return f
2172+
2173+
# Fallback: pure Python in-memory objects
2174+
if isbytes:
2175+
f = io.BytesIO(init if init is not None else b"")
2176+
kind = "bytesio"
2177+
else:
2178+
# newline/text_errors not enforced for StringIO; matches stdlib semantics
2179+
f = io.StringIO(init if init is not None else "")
2180+
kind = "stringio"
21352181

2136-
# Fallback: pure Python in-memory objects
2137-
if isbytes:
2138-
f = io.BytesIO(init if init is not None else b"")
2139-
else:
2140-
# newline not enforced for StringIO; matches stdlib semantics
2141-
f = io.StringIO(init if init is not None else "")
2182+
if reset_to_start:
2183+
f.seek(0)
21422184

2143-
f.seek(0)
2144-
return f
2185+
if on_create is not None:
2186+
on_create(f, kind)
2187+
return f
21452188

21462189
# Helper: wrap a binary file into a text file with encoding/newline
21472190
def _wrap_text(handle):
21482191
# For both Py2 & Py3, TextIOWrapper gives consistent newline/encoding behavior
2149-
tw = io.TextIOWrapper(handle, encoding=encoding, newline=newline)
2150-
return tw
2192+
return io.TextIOWrapper(handle, encoding=encoding,
2193+
newline=newline, errors=text_errors)
21512194

21522195
# -------- Spooled (RAM then disk) --------
21532196
if use_spool:
@@ -2158,17 +2201,30 @@ def _wrap_text(handle):
21582201

21592202
if init is not None:
21602203
f.write(init)
2204+
if reset_to_start:
2205+
f.seek(0)
2206+
elif reset_to_start:
21612207
f.seek(0)
2208+
2209+
if on_create is not None:
2210+
on_create(f, "spool")
21622211
return f
21632212

21642213
# -------- On-disk temp (NamedTemporaryFile) --------
21652214
# Always create binary file; wrap for text if needed for uniform Py2/3 behavior
2166-
b = tempfile.NamedTemporaryFile(mode="w+b", prefix=prefix, suffix=suffix, dir=dir, delete=delete)
2215+
b = tempfile.NamedTemporaryFile(mode="w+b", prefix=prefix, suffix=suffix,
2216+
dir=dir, delete=delete)
21672217
f = b if isbytes else _wrap_text(b)
21682218

21692219
if init is not None:
21702220
f.write(init)
2221+
if reset_to_start:
2222+
f.seek(0)
2223+
elif reset_to_start:
21712224
f.seek(0)
2225+
2226+
if on_create is not None:
2227+
on_create(f, "disk")
21722228
return f
21732229

21742230

0 commit comments

Comments
 (0)