Skip to content

Commit b4857e8

Browse files
committed
Update other open instances of same file on flush
- fixes #302
1 parent d52dfe0 commit b4857e8

File tree

2 files changed

+37
-15
lines changed

2 files changed

+37
-15
lines changed

pyfakefs/fake_filesystem.py

+27-15
Original file line numberDiff line numberDiff line change
@@ -4209,7 +4209,7 @@ def __init__(self, file_object, file_path, update=False, read=False,
42094209
append=False, delete_on_close=False, filesystem=None,
42104210
newline=None, binary=True, closefd=True, encoding=None,
42114211
errors=None, raw_io=False, is_stream=False, use_io=True):
4212-
self._file_object = file_object
4212+
self.file_object = file_object
42134213
self._file_path = file_path
42144214
self._append = append
42154215
self._read = read
@@ -4286,7 +4286,7 @@ def _raise(self, message):
42864286

42874287
def get_object(self):
42884288
"""Return the FakeFile object that is wrapped by the current instance."""
4289-
return self._file_object
4289+
return self.file_object
42904290

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

43014301
# for raw io, all writes are flushed immediately
43024302
if self.allow_update and not self.raw_io:
4303-
self._file_object.set_contents(self._io.getvalue(), self._encoding)
4303+
self.file_object.set_contents(self._io.getvalue(), self._encoding)
43044304
if self._closefd:
43054305
self._filesystem._close_open_file(self.filedes)
43064306
else:
@@ -4313,8 +4313,17 @@ def flush(self):
43134313
self._check_open_file()
43144314
if self.allow_update:
43154315
self._io.flush()
4316-
self._file_object.set_contents(self._io.getvalue(), self._encoding)
4317-
self._file_epoch = self._file_object.epoch
4316+
self.file_object.set_contents(self._io.getvalue(), self._encoding)
4317+
self._file_epoch = self.file_object.epoch
4318+
4319+
if not self.is_stream:
4320+
self._flush_related_files()
4321+
4322+
def _flush_related_files(self):
4323+
for open_files in self._filesystem.open_files[3:]:
4324+
for open_file in open_files:
4325+
if open_file is not self and self.file_object == open_file.file_object:
4326+
open_file._sync_io()
43184327

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

43594368
def _sync_io(self):
43604369
"""Update the stream with changes to the file object contents."""
4361-
if self._file_epoch == self._file_object.epoch:
4370+
if self._file_epoch == self.file_object.epoch:
43624371
return
43634372

43644373
if isinstance(self._io, io.BytesIO):
4365-
contents = self._file_object.byte_contents
4374+
contents = self.file_object.byte_contents
43664375
else:
4367-
contents = self._file_object.contents
4376+
contents = self.file_object.contents
43684377

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

43814390
if is_stream_reader_writer:
43824391
self._io.stream.allow_update = False
4383-
self._file_epoch = self._file_object.epoch
4392+
self._file_epoch = self.file_object.epoch
43844393

43854394
def _read_wrappers(self, name):
43864395
"""Wrap a stream attribute in a read wrapper.
@@ -4468,12 +4477,12 @@ def truncate_wrapper(*args, **kwargs):
44684477
size = io_attr(*args, **kwargs)
44694478
self.flush()
44704479
if not self.is_stream:
4471-
self._file_object.SetSize(size)
4480+
self.file_object.SetSize(size)
44724481
buffer_size = len(self._io.getvalue())
44734482
if buffer_size < size:
44744483
self._io.seek(buffer_size)
44754484
self._io.write('\0' * (size - buffer_size))
4476-
self._file_object.SetContents(self._io.getvalue(), self._encoding)
4485+
self.file_object.SetContents(self._io.getvalue(), self._encoding)
44774486
if sys.version_info[0] > 2:
44784487
return size
44794488

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

44984507
def size(self):
44994508
"""Return the content size in bytes of the wrapped file."""
4500-
return self._file_object.st_size
4509+
return self.file_object.st_size
45014510

45024511
def __getattr__(self, name):
4503-
if self._file_object.is_large_file():
4512+
if self.file_object.is_large_file():
45044513
raise FakeLargeFileIoException(self._file_path)
45054514

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

4591+
def is_stream(self):
4592+
return True
4593+
45824594

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

45874599
def __init__(self, file_object, file_path, filesystem):
4588-
self._file_object = file_object
4600+
self.file_object = file_object
45894601
self._file_path = file_path
45904602
self._filesystem = filesystem
45914603
self.filedes = None
45924604

45934605
def get_object(self):
45944606
"""Return the FakeFile object that is wrapped by the current instance."""
4595-
return self._file_object
4607+
return self.file_object
45964608

45974609
def fileno(self):
45984610
"""Return the file descriptor of the file object."""

tests/fake_open_test.py

+10
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,16 @@ def test_truncate_flushes_contents(self):
757757
f0.truncate()
758758
self.assertEqual(4, self.os.path.getsize(file_path))
759759

760+
def test_update_other_instances_of_same_file_on_flush(self):
761+
# Regression test for #302
762+
file_path = self.make_path('baz')
763+
f0 = self.open(file_path, 'w')
764+
f1 = self.open(file_path, 'w')
765+
f0.write('test')
766+
f0.truncate()
767+
f1.flush()
768+
self.assertEqual(4, self.os.path.getsize(file_path))
769+
760770
def test_that_read_over_end_does_not_reset_position(self):
761771
# Regression test for #286
762772
file_path = self.make_path('baz')

0 commit comments

Comments
 (0)