Skip to content

Commit

Permalink
Correctly handle opening files more than once in connection with file…
Browse files Browse the repository at this point in the history
… handles

- fixes pytest-dev#343
  • Loading branch information
mrbean-bremen committed Jan 11, 2018
1 parent 5aa1ed9 commit 3b65685
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 8 deletions.
25 changes: 17 additions & 8 deletions pyfakefs/fake_filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -1282,10 +1282,10 @@ def _add_open_file(self, file_obj):
"""
if self._free_fd_heap:
open_fd = heapq.heappop(self._free_fd_heap)
self.open_files[open_fd] = file_obj
self.open_files[open_fd] = [file_obj]
return open_fd

self.open_files.append(file_obj)
self.open_files.append([file_obj])
return len(self.open_files) - 1

def _close_open_file(self, file_des):
Expand Down Expand Up @@ -1318,7 +1318,7 @@ def get_open_file(self, file_des):
raise TypeError('an integer is required')
if (file_des >= len(self.open_files) or self.open_files[file_des] is None):
raise OSError(errno.EBADF, 'Bad file descriptor', file_des)
return self.open_files[file_des]
return self.open_files[file_des][0]

def has_open_file(self, file_object):
"""Return True if the given file object is in the list of open files.
Expand All @@ -1329,8 +1329,8 @@ def has_open_file(self, file_object):
Returns:
`True` if the file is open.
"""
return (file_object in [wrapper.get_object()
for wrapper in self.open_files if wrapper])
return (file_object in [wrappers[0].get_object()
for wrappers in self.open_files if wrappers])

def _normalize_path_sep(self, path):
if self.alternative_path_separator is None or not path:
Expand Down Expand Up @@ -4295,13 +4295,16 @@ def fileno(self):
def close(self):
"""Close the file."""
# ignore closing a closed file
if self not in self._filesystem.open_files:
if not self._is_open():
return

# for raw io, all writes are flushed immediately
if self.allow_update and not self.raw_io:
self._file_object.set_contents(self._io.getvalue(), self._encoding)
if self._closefd:
self._filesystem._close_open_file(self.filedes)
else:
self._filesystem.open_files[self.filedes].remove(self)
if self.delete_on_close:
self._filesystem.remove_object(self.get_object().path)

Expand Down Expand Up @@ -4541,8 +4544,14 @@ def write_error(*args, **kwargs):

return getattr(self._io, name)

def _is_open(self):
return (self.filedes < len(self._filesystem.open_files) and
self._filesystem.open_files[self.filedes] is not None and
self in self._filesystem.open_files[self.filedes])


def _check_open_file(self):
if not self.is_stream and not self in self._filesystem.open_files:
if not self.is_stream and not self._is_open():
raise ValueError('I/O operation on closed file')

def __iter__(self):
Expand Down Expand Up @@ -4741,7 +4750,7 @@ def call(self, file_, mode='r', buffering=-1, encoding=None,
if filedes is not None:
fakefile.filedes = filedes
# replace the file wrapper
self.filesystem.open_files[filedes] = fakefile
self.filesystem.open_files[filedes].append(fakefile)
else:
fakefile.filedes = self.filesystem._add_open_file(fakefile)
return fakefile
Expand Down
11 changes: 11 additions & 0 deletions tests/fake_open_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,17 @@ def test_closing_closed_file_does_nothing(self):
f0.close()
self.assertEqual('', f1.read())

def test_closing_file_with_different_close_mode(self):
self.skip_real_fs()
filename = self.make_path('test.txt')
fd = self.os.open(filename, os.O_CREAT | os.O_RDWR)
file_obj = self.filesystem.get_object(filename)
with self.open(fd, 'wb', closefd=False) as fp:
fp.write(b'test')
self.assertTrue(self.filesystem.has_open_file(file_obj))
self.os.close(fd)
self.assertFalse(self.filesystem.has_open_file(file_obj))

def test_truncate_flushes_zeros(self):
# Regression test for #301
file_path = self.make_path('baz')
Expand Down

0 comments on commit 3b65685

Please sign in to comment.