Skip to content

Commit

Permalink
Update other open instances of same file on flush
Browse files Browse the repository at this point in the history
- fixes #302
  • Loading branch information
mrbean-bremen committed Jan 13, 2018
1 parent d52dfe0 commit b4857e8
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 15 deletions.
42 changes: 27 additions & 15 deletions pyfakefs/fake_filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -4209,7 +4209,7 @@ def __init__(self, file_object, file_path, update=False, read=False,
append=False, delete_on_close=False, filesystem=None,
newline=None, binary=True, closefd=True, encoding=None,
errors=None, raw_io=False, is_stream=False, use_io=True):
self._file_object = file_object
self.file_object = file_object
self._file_path = file_path
self._append = append
self._read = read
Expand Down Expand Up @@ -4286,7 +4286,7 @@ def _raise(self, message):

def get_object(self):
"""Return the FakeFile object that is wrapped by the current instance."""
return self._file_object
return self.file_object

def fileno(self):
"""Return the file descriptor of the file object."""
Expand All @@ -4300,7 +4300,7 @@ def close(self):

# 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)
self.file_object.set_contents(self._io.getvalue(), self._encoding)
if self._closefd:
self._filesystem._close_open_file(self.filedes)
else:
Expand All @@ -4313,8 +4313,17 @@ def flush(self):
self._check_open_file()
if self.allow_update:
self._io.flush()
self._file_object.set_contents(self._io.getvalue(), self._encoding)
self._file_epoch = self._file_object.epoch
self.file_object.set_contents(self._io.getvalue(), self._encoding)
self._file_epoch = self.file_object.epoch

if not self.is_stream:
self._flush_related_files()

def _flush_related_files(self):
for open_files in self._filesystem.open_files[3:]:
for open_file in open_files:
if open_file is not self and self.file_object == open_file.file_object:
open_file._sync_io()

def seek(self, offset, whence=0):
"""Move read/write pointer in 'file'."""
Expand Down Expand Up @@ -4358,13 +4367,13 @@ def _flushes_after_tell(self):

def _sync_io(self):
"""Update the stream with changes to the file object contents."""
if self._file_epoch == self._file_object.epoch:
if self._file_epoch == self.file_object.epoch:
return

if isinstance(self._io, io.BytesIO):
contents = self._file_object.byte_contents
contents = self.file_object.byte_contents
else:
contents = self._file_object.contents
contents = self.file_object.contents

is_stream_reader_writer = isinstance(self._io, codecs.StreamReaderWriter)
if is_stream_reader_writer:
Expand All @@ -4380,7 +4389,7 @@ def _sync_io(self):

if is_stream_reader_writer:
self._io.stream.allow_update = False
self._file_epoch = self._file_object.epoch
self._file_epoch = self.file_object.epoch

def _read_wrappers(self, name):
"""Wrap a stream attribute in a read wrapper.
Expand Down Expand Up @@ -4468,12 +4477,12 @@ def truncate_wrapper(*args, **kwargs):
size = io_attr(*args, **kwargs)
self.flush()
if not self.is_stream:
self._file_object.SetSize(size)
self.file_object.SetSize(size)
buffer_size = len(self._io.getvalue())
if buffer_size < size:
self._io.seek(buffer_size)
self._io.write('\0' * (size - buffer_size))
self._file_object.SetContents(self._io.getvalue(), self._encoding)
self.file_object.SetContents(self._io.getvalue(), self._encoding)
if sys.version_info[0] > 2:
return size

Expand All @@ -4497,10 +4506,10 @@ def write_wrapper(*args, **kwargs):

def size(self):
"""Return the content size in bytes of the wrapped file."""
return self._file_object.st_size
return self.file_object.st_size

def __getattr__(self, name):
if self._file_object.is_large_file():
if self.file_object.is_large_file():
raise FakeLargeFileIoException(self._file_path)

reading = name.startswith('read') or name == 'next'
Expand Down Expand Up @@ -4579,20 +4588,23 @@ def close(self):
"""We do not support closing standard streams."""
pass

def is_stream(self):
return True


class FakeDirWrapper(object):
"""Wrapper for a FakeDirectory object to be used in open files list.
"""

def __init__(self, file_object, file_path, filesystem):
self._file_object = file_object
self.file_object = file_object
self._file_path = file_path
self._filesystem = filesystem
self.filedes = None

def get_object(self):
"""Return the FakeFile object that is wrapped by the current instance."""
return self._file_object
return self.file_object

def fileno(self):
"""Return the file descriptor of the file object."""
Expand Down
10 changes: 10 additions & 0 deletions tests/fake_open_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,16 @@ def test_truncate_flushes_contents(self):
f0.truncate()
self.assertEqual(4, self.os.path.getsize(file_path))

def test_update_other_instances_of_same_file_on_flush(self):
# Regression test for #302
file_path = self.make_path('baz')
f0 = self.open(file_path, 'w')
f1 = self.open(file_path, 'w')
f0.write('test')
f0.truncate()
f1.flush()
self.assertEqual(4, self.os.path.getsize(file_path))

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

0 comments on commit b4857e8

Please sign in to comment.