diff --git a/pyfakefs/fake_filesystem.py b/pyfakefs/fake_filesystem.py index 370bdc87..85fb1b05 100644 --- a/pyfakefs/fake_filesystem.py +++ b/pyfakefs/fake_filesystem.py @@ -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 @@ -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.""" @@ -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: @@ -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'.""" @@ -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: @@ -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. @@ -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 @@ -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' @@ -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.""" diff --git a/tests/fake_open_test.py b/tests/fake_open_test.py index 828eb8dc..637d0b70 100644 --- a/tests/fake_open_test.py +++ b/tests/fake_open_test.py @@ -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')