Skip to content

Commit

Permalink
Fixed recursion error on unpickling the fake file system
Browse files Browse the repository at this point in the history
- changed FakeStatResult so that pickling works under Python 2
- fixes #445
  • Loading branch information
mrbean-bremen committed Oct 25, 2018
1 parent 71f102c commit 9a88cbb
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 18 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The release versions are PyPi releases.
#### New Features

#### Fixes
* fixed recursion error on unpickling the fake file system
([#445](../../issues/445))

## [Version 3.5](https://pypi.python.org/pypi/pyfakefs/3.5)

Expand Down
19 changes: 9 additions & 10 deletions pyfakefs/fake_filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,35 +288,32 @@ def contents(self):
@property
def st_ctime(self):
"""Return the creation time of the fake file."""
return (self._st_ctime if FakeOsModule.stat_float_times()
else int(self._st_ctime))
return self.stat_result.st_ctime

@property
def st_atime(self):
"""Return the access time of the fake file."""
return (self._st_atime if FakeOsModule.stat_float_times()
else int(self._st_atime))
return self.stat_result.st_atime

@property
def st_mtime(self):
"""Return the modification time of the fake file."""
return (self._st_mtime if FakeOsModule.stat_float_times()
else int(self._st_mtime))
return self.stat_result.st_mtime

@st_ctime.setter
def st_ctime(self, val):
"""Set the creation time of the fake file."""
self._st_ctime = val
self.stat_result.st_ctime = val

@st_atime.setter
def st_atime(self, val):
"""Set the access time of the fake file."""
self._st_atime = val
self.stat_result.st_atime = val

@st_mtime.setter
def st_mtime(self, val):
"""Set the modification time of the fake file."""
self._st_mtime = val
self.stat_result.st_mtime = val

def set_large_file_size(self, st_size):
"""Sets the self.st_size attribute and replaces self.content with None.
Expand Down Expand Up @@ -510,7 +507,9 @@ def SetCTime(self, st_ctime):

def __getattr__(self, item):
"""Forward some properties to stat_result."""
return getattr(self.stat_result, item)
if item in self.stat_types:
return getattr(self.stat_result, item)
return super(FakeFile, self).__getattr__(item)

def __setattr__(self, key, value):
"""Forward some properties to stat_result."""
Expand Down
25 changes: 17 additions & 8 deletions pyfakefs/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class FakeStatResult(object):
_stat_float_times = sys.version_info >= (2, 5)

def __init__(self, is_windows, initial_time=None):
self.use_float = self.stat_float_times
self._use_float = None
self.st_mode = None
self.st_ino = None
self.st_dev = None
Expand All @@ -87,6 +87,16 @@ def __init__(self, is_windows, initial_time=None):
self._st_mtime_ns = self._st_atime_ns
self._st_ctime_ns = self._st_atime_ns

@property
def use_float(self):
if self._use_float is None:
return self.stat_float_times()
return self._use_float

@use_float.setter
def use_float(self, val):
self._use_float = val

def __eq__(self, other):
return (
isinstance(other, FakeStatResult) and
Expand All @@ -106,12 +116,11 @@ def __ne__(self, other):
return not self == other

def copy(self):
"""Return a copy where the float usage is hard-coded to mimic the behavior
of the real os.stat_result.
"""Return a copy where the float usage is hard-coded to mimic the
behavior of the real os.stat_result.
"""
use_float = self.use_float()
stat_result = copy(self)
stat_result.use_float = lambda: use_float
stat_result.use_float = self.use_float
return stat_result

def set_from_stat_result(self, stat_result):
Expand Down Expand Up @@ -152,19 +161,19 @@ def stat_float_times(cls, newvalue=None):
def st_ctime(self):
"""Return the creation time in seconds."""
ctime = self._st_ctime_ns / 1e9
return ctime if self.use_float() else int(ctime)
return ctime if self.use_float else int(ctime)

@property
def st_atime(self):
"""Return the access time in seconds."""
atime = self._st_atime_ns / 1e9
return atime if self.use_float() else int(atime)
return atime if self.use_float else int(atime)

@property
def st_mtime(self):
"""Return the modification time in seconds."""
mtime = self._st_mtime_ns / 1e9
return mtime if self.use_float() else int(mtime)
return mtime if self.use_float else int(mtime)

@st_ctime.setter
def st_ctime(self, val):
Expand Down
9 changes: 9 additions & 0 deletions pyfakefs/tests/fake_filesystem_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,15 @@ def test_directory_access_on_file_posix(self):
self.filesystem.is_windows_fs = False
self.check_directory_access_on_file(errno.ENOTDIR)

def test_pickle_fs(self):
"""Regression test for #445"""
import pickle
self.filesystem.open_files = []
p = pickle.dumps(self.filesystem)
fs = pickle.loads(p)
self.assertEqual(str(fs.root), str(self.filesystem.root))
self.assertEqual(fs.mount_points, self.filesystem.mount_points)


class CaseInsensitiveFakeFilesystemTest(TestCase):
def setUp(self):
Expand Down

0 comments on commit 9a88cbb

Please sign in to comment.