Skip to content

Commit efd90e7

Browse files
committed
Fix os.file for unreadable files
- set regular file mode if none set in FakeFile st_mode arg - add convenience permission argument to create_file() - fixes #588
1 parent 40416a1 commit efd90e7

File tree

5 files changed

+30
-8
lines changed

5 files changed

+30
-8
lines changed

CHANGES.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
The released versions correspond to PyPi releases.
33

44
## Version 4.4.0 (as yet unreleased)
5-
5+
66
#### New Features
77
* added support for `pathlib.Path.link_to` (new in Python 3.8)
88
(see [#580](../../issues/580))
@@ -16,7 +16,9 @@ The released versions correspond to PyPi releases.
1616
(see [#581](../../issues/581))
1717
* added non-functional argument `effective_ids` to `os.access`
1818
(see [#585](../../issues/585))
19-
19+
* correctly handle `os.file` for unreadable files
20+
(see [#588](../../issues/588))
21+
2022
### Infrastructure
2123
* added automatic documentation build and check-in
2224

pyfakefs/fake_filesystem.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,9 @@ def __init__(self, name, st_mode=S_IFREG | PERM_DEF_FILE,
269269
Args:
270270
name: Name of the file/directory, without parent path information
271271
st_mode: The stat.S_IF* constant representing the file type (i.e.
272-
stat.S_IFREG, stat.S_IFDIR)
272+
stat.S_IFREG, stat.S_IFDIR), and the file permissions.
273+
If no file type is set (e.g. permission flags only), a
274+
regular file type is assumed.
273275
contents: The contents of the filesystem object; should be a string
274276
or byte object for regular files, and a list of other
275277
FakeFile or FakeDirectory objects for FakeDirectory objects
@@ -288,6 +290,8 @@ def __init__(self, name, st_mode=S_IFREG | PERM_DEF_FILE,
288290
self.name = name
289291
self.stat_result = FakeStatResult(
290292
filesystem.is_windows_fs, USER_ID, GROUP_ID, time.time())
293+
if st_mode >> 12 == 0:
294+
st_mode |= S_IFREG
291295
self.stat_result.st_mode = st_mode
292296
self.encoding = encoding
293297
self.errors = errors or 'strict'
@@ -2906,14 +2910,17 @@ def makedirs(self, dir_name, mode=PERM_DEF, exist_ok=False):
29062910
e.errno = errno.ENOENT
29072911
self.raise_os_error(e.errno, e.filename)
29082912

2909-
def _is_of_type(self, path, st_flag, follow_symlinks=True):
2913+
def _is_of_type(self, path, st_flag, follow_symlinks=True,
2914+
check_read_perm=True):
29102915
"""Helper function to implement isdir(), islink(), etc.
29112916
29122917
See the stat(2) man page for valid stat.S_I* flag values
29132918
29142919
Args:
29152920
path: Path to file to stat and test
29162921
st_flag: The stat.S_I* flag checked for the file's st_mode
2922+
check_read_perm: If True (default) False is returned for
2923+
existing but unreadable file paths.
29172924
29182925
Returns:
29192926
(boolean) `True` if the st_flag is set in path's st_mode.
@@ -2925,7 +2932,8 @@ def _is_of_type(self, path, st_flag, follow_symlinks=True):
29252932
if path is None:
29262933
raise TypeError
29272934
try:
2928-
obj = self.resolve(path, follow_symlinks)
2935+
obj = self.resolve(path, follow_symlinks,
2936+
check_read_perm=check_read_perm)
29292937
if obj:
29302938
self.raise_for_filepath_ending_with_separator(
29312939
path, obj, macos_handling=not follow_symlinks)
@@ -2960,7 +2968,8 @@ def isfile(self, path, follow_symlinks=True):
29602968
Raises:
29612969
TypeError: if path is None.
29622970
"""
2963-
return self._is_of_type(path, S_IFREG, follow_symlinks)
2971+
return self._is_of_type(path, S_IFREG, follow_symlinks,
2972+
check_read_perm=False)
29642973

29652974
def islink(self, path):
29662975
"""Determine if path identifies a symbolic link.

pyfakefs/tests/fake_os_test.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,11 @@ def test_isfile_with_trailing_separator_macos(self):
547547
self.create_file(file_path)
548548
self.assertFalse(self.os.path.isfile(file_path + self.os.sep))
549549

550+
def test_isfile_not_readable_file(self):
551+
file_path = self.make_path('foo')
552+
self.create_file(file_path, perm=0)
553+
self.assertTrue(self.os.path.isfile(file_path))
554+
550555
def check_stat_with_trailing_separator(self, error_nr):
551556
# regression test for #376
552557
file_path = self.make_path('foo')

pyfakefs/tests/fake_pathlib_test.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,12 @@ def test_isfile(self):
879879
self.assertEqual(self.os.path.isfile(path),
880880
self.os.path.isfile(self.path(path)))
881881

882+
def test_isfile_not_readable(self):
883+
path = self.make_path('foo', 'bar', 'baz')
884+
self.create_file(path, perm=0)
885+
self.assertEqual(self.os.path.isfile(path),
886+
self.os.path.isfile(self.path(path)))
887+
882888
def test_islink(self):
883889
path = self.make_path('foo', 'bar', 'baz')
884890
self.create_file(path)

pyfakefs/tests/test_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ def create_dir(self, dir_path):
295295
self.os.mkdir(existing_path)
296296
self.os.chmod(existing_path, 0o777)
297297

298-
def create_file(self, file_path, contents=None, encoding=None):
298+
def create_file(self, file_path, contents=None, encoding=None, perm=0o666):
299299
"""Create the given file at `file_path` with optional contents,
300300
including subdirectories. `file_path` shall be composed using
301301
`make_path()`.
@@ -309,7 +309,7 @@ def create_file(self, file_path, contents=None, encoding=None):
309309
with self.open(file_path, mode) as f:
310310
if contents is not None:
311311
f.write(contents)
312-
self.os.chmod(file_path, 0o666)
312+
self.os.chmod(file_path, perm)
313313

314314
def create_symlink(self, link_path, target_path):
315315
"""Create the path at `link_path`, and a symlink to this path at

0 commit comments

Comments
 (0)