Skip to content

Commit 74d180e

Browse files
authored
Add files via upload
1 parent 5b5030a commit 74d180e

1 file changed

Lines changed: 94 additions & 39 deletions

File tree

pyfoxfile.py

Lines changed: 94 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2067,31 +2067,48 @@ def MkTempFile(data=None,
20672067
prefix=__program_name__,
20682068
delete=True,
20692069
encoding="utf-8",
2070-
newline=None, # text mode only; in-memory objects ignore newline semantics
2070+
newline=None,
2071+
text_errors="strict",
20712072
dir=None,
20722073
suffix="",
20732074
use_spool=__use_spoolfile__,
2075+
autoswitch_spool=False,
20742076
spool_max=__spoolfile_size__,
2075-
spool_dir=__use_spooldir__):
2077+
spool_dir=__use_spooldir__,
2078+
reset_to_start=True,
2079+
memfd_name=None,
2080+
memfd_allow_sealing=False,
2081+
memfd_flags_extra=0,
2082+
on_create=None):
20762083
"""
20772084
Return a file-like handle with consistent behavior on Py2.7 and Py3.x.
20782085

20792086
Storage:
2080-
- inmem=True -> BytesIO (bytes) or StringIO (text), or memfd for bytes if available
2081-
- inmem=False, use_spool=True -> SpooledTemporaryFile (binary), optionally TextIOWrapper for text
2082-
- inmem=False, use_spool=False -> NamedTemporaryFile (binary), optionally TextIOWrapper for text
2087+
- inmem=True, usememfd=True, isbytes=True and memfd available
2088+
-> memfd-backed anonymous file (binary)
2089+
- inmem=True, otherwise
2090+
-> BytesIO (bytes) or StringIO (text)
2091+
- inmem=False, use_spool=True
2092+
-> SpooledTemporaryFile (binary), optionally TextIOWrapper for text
2093+
- inmem=False, use_spool=False
2094+
-> NamedTemporaryFile (binary), optionally TextIOWrapper for text
20832095

20842096
Text vs bytes:
20852097
- isbytes=True -> file expects bytes; 'data' must be bytes-like
2086-
- isbytes=False -> file expects text; 'data' must be text (unicode/str). Newline translation and encoding
2087-
apply only for spooled/named files (not BytesIO/StringIO).
2098+
- isbytes=False -> file expects text; 'data' must be text (unicode/str). Newline translation and
2099+
encoding apply only for spooled/named files (not BytesIO/StringIO).
20882100

20892101
Notes:
2090-
- On Windows, NamedTemporaryFile(delete=True) keeps the file open and cannot be reopened by other processes.
2091-
Use delete=False if you need to pass the path elsewhere.
2092-
- For text: in-memory StringIO ignores 'newline' (as usual).
2093-
- When available, memfd is used only for inmem=True and isbytes=True, providing an anonymous in-memory
2094-
file descriptor (Linux-only). Text in-memory still uses StringIO to preserve newline semantics.
2102+
- On Windows, NamedTemporaryFile(delete=True) keeps the file open and cannot be reopened by
2103+
other processes. Use delete=False if you need to pass the path elsewhere.
2104+
- For text: in-memory StringIO ignores 'newline' and 'text_errors' (as usual).
2105+
- When available, and if usememfd=True, memfd is used only for inmem=True and isbytes=True,
2106+
providing an anonymous in-memory file descriptor (Linux-only). Text in-memory still uses
2107+
StringIO to preserve newline semantics.
2108+
- If autoswitch_spool=True and initial data size exceeds spool_max, in-memory storage is
2109+
skipped and a spooled file is used instead (if use_spool=True).
2110+
- If on_create is not None, it is called as on_create(fp, kind) where kind is one of:
2111+
"memfd", "bytesio", "stringio", "spool", "disk".
20952112
"""
20962113

20972114
# -- sanitize simple params (avoid None surprises) --
@@ -2123,39 +2140,65 @@ def MkTempFile(data=None,
21232140
else:
21242141
init = None
21252142

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

2144-
# Fallback: pure Python in-memory objects
2145-
if isbytes:
2146-
f = io.BytesIO(init if init is not None else b"")
2147-
else:
2148-
# newline not enforced for StringIO; matches stdlib semantics
2149-
f = io.StringIO(init if init is not None else "")
2190+
if reset_to_start:
2191+
f.seek(0)
21502192

2151-
f.seek(0)
2152-
return f
2193+
if on_create is not None:
2194+
on_create(f, kind)
2195+
return f
21532196

21542197
# Helper: wrap a binary file into a text file with encoding/newline
21552198
def _wrap_text(handle):
21562199
# For both Py2 & Py3, TextIOWrapper gives consistent newline/encoding behavior
2157-
tw = io.TextIOWrapper(handle, encoding=encoding, newline=newline)
2158-
return tw
2200+
return io.TextIOWrapper(handle, encoding=encoding,
2201+
newline=newline, errors=text_errors)
21592202

21602203
# -------- Spooled (RAM then disk) --------
21612204
if use_spool:
@@ -2166,19 +2209,31 @@ def _wrap_text(handle):
21662209

21672210
if init is not None:
21682211
f.write(init)
2212+
if reset_to_start:
2213+
f.seek(0)
2214+
elif reset_to_start:
21692215
f.seek(0)
2216+
2217+
if on_create is not None:
2218+
on_create(f, "spool")
21702219
return f
21712220

21722221
# -------- On-disk temp (NamedTemporaryFile) --------
21732222
# Always create binary file; wrap for text if needed for uniform Py2/3 behavior
2174-
b = tempfile.NamedTemporaryFile(mode="w+b", prefix=prefix, suffix=suffix, dir=dir, delete=delete)
2223+
b = tempfile.NamedTemporaryFile(mode="w+b", prefix=prefix, suffix=suffix,
2224+
dir=dir, delete=delete)
21752225
f = b if isbytes else _wrap_text(b)
21762226

21772227
if init is not None:
21782228
f.write(init)
2229+
if reset_to_start:
2230+
f.seek(0)
2231+
elif reset_to_start:
21792232
f.seek(0)
2180-
return f
21812233

2234+
if on_create is not None:
2235+
on_create(f, "disk")
2236+
return f
21822237

21832238

21842239
def RemoveWindowsPath(dpath):

0 commit comments

Comments
 (0)