Skip to content

Commit 4ad2231

Browse files
committed
System can now deal with file descriptors as input, but it still requires some more testing. Also fds need to remain open to be usable for new mapped regions
1 parent 8d64e74 commit 4ad2231

3 files changed

Lines changed: 63 additions & 25 deletions

File tree

smmap/mman.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def _destroy(self):
5454
num_clients = self._rlist.client_count() - 2
5555
if num_clients == 0 and len(self._rlist) == 0:
5656
# Free all resources associated with the mapped file
57-
self._manager._fdict.pop(self._rlist.path())
57+
self._manager._fdict.pop(self._rlist.path_or_fd())
5858
#END remove regions list from manager
5959
#END handle regions
6060

@@ -190,7 +190,7 @@ def use_region(self, offset, size, flags = 0, _is_recursive=False):
190190
if man._handle_count >= man._max_handle_count:
191191
raise Exception
192192
#END assert own imposed max file handles
193-
self._region = self.MappedRegionCls(a.path(), mid.ofs, mid.size, flags)
193+
self._region = self.MappedRegionCls(a.path_or_fd(), mid.ofs, mid.size, flags)
194194
except Exception:
195195
# apparently we are out of system resources or hit a limit
196196
# As many more operations are likely to fail in that condition (
@@ -278,9 +278,26 @@ def file_size(self):
278278
""":return: size of the underlying file"""
279279
return self._rlist.file_size()
280280

281+
def path_or_fd(self):
282+
""":return: path or file decriptor of the underlying mapped file"""
283+
return self._rlist.path_or_fd()
284+
281285
def path(self):
282-
""":return: path of the underlying mapped file"""
283-
return self._rlist.path()
286+
""":return: path of the underlying mapped file
287+
:raise ValueError: if attached path is not a path"""
288+
if isinstance(self._rlist.path_or_fd(), int):
289+
raise ValueError("Path queried although mapping was applied to a file descriptor")
290+
# END handle type
291+
return self._rlist.path_or_fd()
292+
293+
def fd(self):
294+
""":return: file descriptor used to create the underlying mapping.
295+
:note: it is not required to be valid anymore
296+
:raise ValueError: if the mapping was not created by a file descriptor"""
297+
if isinstance(self._rlist.path_or_fd(), basestring):
298+
return ValueError("File descriptor queried although mapping was generated from path")
299+
#END handle type
300+
return self._rlist.path_or_fd()
284301

285302
#} END interface
286303

@@ -383,12 +400,18 @@ def _collect_lru_region(self, size):
383400
return num_found
384401

385402
#{ Interface
386-
def make_cursor(self, path):
387-
""":return: a cursor pointing to the given path. It can be used to map new regions of the file into memory"""
388-
regions = self._fdict.get(path)
403+
def make_cursor(self, path_or_fd):
404+
""":return: a cursor pointing to the given path or file descriptor.
405+
It can be used to map new regions of the file into memory
406+
:note: if a file descriptor is given, it is assumed to be open and valid,
407+
but may be closed afterwards. To refer to the same file, you may reuse
408+
your existing file descriptor, but keep in mind that new windows can only
409+
be mapped as long as it stays valid. This is why the using actual file paths
410+
are preferred unless you plan to keep the file descriptor open."""
411+
regions = self._fdict.get(path_or_fd)
389412
if regions is None:
390-
regions = self.MappedRegionListCls(path)
391-
self._fdict[path] = regions
413+
regions = self.MappedRegionListCls(path_or_fd)
414+
self._fdict[path_or_fd] = regions
392415
# END obtain region for path
393416
return MemoryCursor(self, regions)
394417

smmap/test/test_util.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from smmap.util import *
44

5+
import os
56
import sys
67

78
class TestMMan(TestBase):
@@ -86,13 +87,18 @@ def test_region(self):
8687

8788
def test_region_list(self):
8889
fc = FileCreator(100, "sample_file")
89-
ml = MappedRegionList(fc.path)
9090

91-
assert ml.client_count() == 1
92-
93-
assert len(ml) == 0
94-
assert ml.path() == fc.path
95-
assert ml.file_size() == fc.size
91+
fd = os.open(fc.path, os.O_RDONLY)
92+
for item in (fc.path, fd):
93+
ml = MappedRegionList(item)
94+
95+
assert ml.client_count() == 1
96+
97+
assert len(ml) == 0
98+
assert ml.path_or_fd() == item
99+
assert ml.file_size() == fc.size
100+
#END handle input
101+
os.close(fd)
96102

97103
def test_util(self):
98104
assert isinstance(is_64_bit(), bool) # just call it

smmap/util.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ class MappedRegion(object):
9393
#END handle additional slot
9494

9595

96-
def __init__(self, path, ofs, size, flags = 0):
96+
def __init__(self, path_or_fd, ofs, size, flags = 0):
9797
"""Initialize a region, allocate the memory map
98-
:param path: path to the file to map
98+
:param path_or_fd: path to the file to map, or the opened file descriptor
9999
:param ofs: **aligned** offset into the file to be mapped
100100
:param size: if size is larger then the file on disk, the whole file will be
101101
allocated the the size automatically adjusted
@@ -105,7 +105,12 @@ def __init__(self, path, ofs, size, flags = 0):
105105
self._size = 0
106106
self._uc = 0
107107

108-
fd = os.open(path, os.O_RDONLY|getattr(os, 'O_BINARY', 0)|flags)
108+
if isinstance(path_or_fd, int):
109+
fd = path_or_fd
110+
else:
111+
fd = os.open(path_or_fd, os.O_RDONLY|getattr(os, 'O_BINARY', 0)|flags)
112+
#END handle fd
113+
109114
try:
110115
kwargs = dict(access=ACCESS_READ, offset=ofs)
111116
corrected_size = size
@@ -189,29 +194,33 @@ def includes_ofs(self, ofs):
189194
class MappedRegionList(list):
190195
"""List of MappedRegion instances associating a path with a list of regions."""
191196
__slots__ = (
192-
'_path', # path which is mapped by all our regions
197+
'_path_or_fd', # path or file descriptor which is mapped by all our regions
193198
'_file_size' # total size of the file we map
194199
)
195200

196201
def __new__(cls, path):
197202
return super(MappedRegionList, cls).__new__(cls)
198203

199-
def __init__(self, path):
200-
self._path = path
204+
def __init__(self, path_or_fd):
205+
self._path_or_fd = path_or_fd
201206
self._file_size = None
202207

203208
def client_count(self):
204209
""":return: amount of clients which hold a reference to this instance"""
205210
return getrefcount(self)-3
206211

207-
def path(self):
208-
""":return: path to file whose regions we manage"""
209-
return self._path
212+
def path_or_fd(self):
213+
""":return: path or file descriptor we are attached to"""
214+
return self._path_or_fd
210215

211216
def file_size(self):
212217
""":return: size of file we manager"""
213218
if self._file_size is None:
214-
self._file_size = os.stat(self._path).st_size
219+
if isinstance(self._path_or_fd, basestring):
220+
self._file_size = os.stat(self._path_or_fd).st_size
221+
else:
222+
self._file_size = os.fstat(self._path_or_fd).st_size
223+
#END handle path type
215224
#END update file size
216225
return self._file_size
217226

0 commit comments

Comments
 (0)