Skip to content

Commit 6278f79

Browse files
committed
Give an actual name to MemoryIO on FreeBSD
1 parent 8f4ba33 commit 6278f79

File tree

1 file changed

+56
-16
lines changed

1 file changed

+56
-16
lines changed

dmoj/cptbox/utils.py

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,65 @@
22
import io
33
import mmap
44
import os
5+
from tempfile import NamedTemporaryFile
56
from typing import Optional
67

78
from dmoj.cptbox._cptbox import memory_fd_create, memory_fd_seal
89

910

10-
class MemoryIO(io.FileIO):
11-
def __init__(self, prefill: Optional[bytes] = None, seal=False) -> None:
12-
super().__init__(memory_fd_create(), 'r+')
11+
def _make_fd_readonly(fd):
12+
new_fd = os.open(f'/proc/self/fd/{fd}', os.O_RDONLY)
13+
try:
14+
os.dup2(new_fd, fd)
15+
finally:
16+
os.close(new_fd)
17+
18+
19+
class MmapableIO(io.FileIO):
20+
def __init__(self, fd, *, prefill: Optional[bytes] = None, seal=False) -> None:
21+
super().__init__(fd, 'r+')
22+
1323
if prefill:
1424
self.write(prefill)
1525
if seal:
1626
self.seal()
1727

28+
def to_bytes(self) -> bytes:
29+
try:
30+
with mmap.mmap(self.fileno(), 0, access=mmap.ACCESS_READ) as f:
31+
return bytes(f)
32+
except ValueError as e:
33+
if e.args[0] == 'cannot mmap an empty file':
34+
return b''
35+
raise
36+
37+
38+
class NamedFileIO(MmapableIO):
39+
_name: str
40+
41+
def __init__(self, *, prefill: Optional[bytes] = None, seal=False) -> None:
42+
with NamedTemporaryFile(delete=False) as f:
43+
self._name = f.name
44+
super().__init__(os.dup(f.fileno()), prefill=prefill, seal=seal)
45+
46+
def seal(self) -> None:
47+
self.seek(0, os.SEEK_SET)
48+
_make_fd_readonly(self.fileno())
49+
50+
def close(self) -> None:
51+
super().close()
52+
os.unlink(self._name)
53+
54+
def to_path(self) -> str:
55+
return self._name
56+
57+
58+
class MemoryIO(MmapableIO):
59+
_name: Optional[str] = None
60+
61+
def __init__(self, *, prefill: Optional[bytes] = None, seal=False) -> None:
62+
super().__init__(memory_fd_create(), prefill=prefill, seal=seal)
63+
1864
def seal(self) -> None:
1965
fd = self.fileno()
2066
try:
@@ -26,20 +72,14 @@ def seal(self) -> None:
2672
return
2773
raise
2874

29-
new_fd = os.open(f'/proc/self/fd/{fd}', os.O_RDONLY)
30-
try:
31-
os.dup2(new_fd, fd)
32-
finally:
33-
os.close(new_fd)
75+
_make_fd_readonly(fd)
3476

3577
def to_path(self) -> str:
3678
return f'/proc/{os.getpid()}/fd/{self.fileno()}'
3779

38-
def to_bytes(self) -> bytes:
39-
try:
40-
with mmap.mmap(self.fileno(), 0, access=mmap.ACCESS_READ) as f:
41-
return bytes(f)
42-
except ValueError as e:
43-
if e.args[0] == 'cannot mmap an empty file':
44-
return b''
45-
raise
80+
81+
# On FreeBSD and some other systems, if /proc/[pid]/fd doesn't exist,
82+
# then MemoryIO.to_path() will not work. We fall back to NamedFileIO
83+
# in that case.
84+
if not os.path.isdir(f'/proc/{os.getpid()}/fd'):
85+
MemoryIO = NamedFileIO

0 commit comments

Comments
 (0)