Skip to content

Commit 16e7600

Browse files
committed
Fix handling of umask
- umask is now applied per default
1 parent 5283be6 commit 16e7600

File tree

5 files changed

+66
-39
lines changed

5 files changed

+66
-39
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ The released versions correspond to PyPI releases.
3030
* fixed permission problem with `shutil.rmtree` if emulating Windows under POSIX
3131
(see [#979](../../issues/979))
3232
* fixed handling of errors on opening files via file descriptor (see [#967](../../issues/967))
33+
* fixed handling of `umask` - it is now applied by default
3334

3435
### Infrastructure
3536
* replace `undefined` by own minimal implementation to avoid importing it

pyfakefs/fake_filesystem.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -2117,7 +2117,7 @@ def create_dir(
21172117
# set the permission after creating the directories
21182118
# to allow directory creation inside a read-only directory
21192119
for new_dir in new_dirs:
2120-
new_dir.st_mode = S_IFDIR | perm_bits
2120+
new_dir.st_mode = S_IFDIR | (perm_bits & ~self.umask)
21212121

21222122
return current_dir
21232123

@@ -2128,7 +2128,7 @@ def create_file(
21282128
contents: AnyString = "",
21292129
st_size: Optional[int] = None,
21302130
create_missing_dirs: bool = True,
2131-
apply_umask: bool = False,
2131+
apply_umask: bool = True,
21322132
encoding: Optional[str] = None,
21332133
errors: Optional[str] = None,
21342134
side_effect: Optional[Callable] = None,
@@ -2403,7 +2403,7 @@ def create_file_internally(
24032403
contents: AnyString = "",
24042404
st_size: Optional[int] = None,
24052405
create_missing_dirs: bool = True,
2406-
apply_umask: bool = False,
2406+
apply_umask: bool = True,
24072407
encoding: Optional[str] = None,
24082408
errors: Optional[str] = None,
24092409
read_from_real_fs: bool = False,
@@ -2543,6 +2543,7 @@ def create_symlink(
25432543
st_mode=S_IFLNK | helpers.PERM_DEF,
25442544
contents=link_target_path,
25452545
create_missing_dirs=create_missing_dirs,
2546+
apply_umask=self.is_macos,
25462547
)
25472548

25482549
def create_link(
@@ -2790,7 +2791,7 @@ def makedirs(
27902791
else:
27912792
current_dir = cast(FakeDirectory, current_dir.entries[component])
27922793
try:
2793-
self.create_dir(dir_name, mode & ~self.umask)
2794+
self.create_dir(dir_name, mode)
27942795
except OSError as e:
27952796
if e.errno == errno.EACCES:
27962797
# permission denied - propagate exception

pyfakefs/tests/fake_os_test.py

+36-26
Original file line numberDiff line numberDiff line change
@@ -40,26 +40,36 @@
4040

4141

4242
class FakeOsModuleTestBase(RealFsTestCase):
43+
def setUp(self):
44+
super().setUp()
45+
self.umask = self.os.umask(0o022)
46+
47+
def tearDown(self):
48+
self.os.umask(self.umask)
49+
4350
def createTestFile(self, path):
4451
self.create_file(path)
4552
self.assertTrue(self.os.path.exists(path))
4653
st = self.os.stat(path)
47-
self.assertEqual(0o666, stat.S_IMODE(st.st_mode))
54+
# under Windows, the umask has no effect
55+
mode = 0o666 if self.is_windows_fs else 0o0644
56+
self.assertEqual(mode, stat.S_IMODE(st.st_mode))
4857
self.assertTrue(st.st_mode & stat.S_IFREG)
4958
self.assertFalse(st.st_mode & stat.S_IFDIR)
5059

5160
def createTestDirectory(self, path):
5261
self.create_dir(path)
5362
self.assertTrue(self.os.path.exists(path))
5463
st = self.os.stat(path)
55-
self.assertEqual(0o777, stat.S_IMODE(st.st_mode))
64+
mode = 0o777 if self.is_windows_fs else 0o755
65+
self.assertEqual(mode, stat.S_IMODE(st.st_mode))
5666
self.assertFalse(st.st_mode & stat.S_IFREG)
5767
self.assertTrue(st.st_mode & stat.S_IFDIR)
5868

5969

6070
class FakeOsModuleTest(FakeOsModuleTestBase):
6171
def setUp(self):
62-
super(FakeOsModuleTest, self).setUp()
72+
super().setUp()
6373
self.rwx = self.os.R_OK | self.os.W_OK | self.os.X_OK
6474
self.rw = self.os.R_OK | self.os.W_OK
6575

@@ -2082,7 +2092,8 @@ def test_chmod_no_follow_symlink(self):
20822092
else:
20832093
self.os.chmod(link_path, 0o6543, follow_symlinks=False)
20842094
st = self.os.stat(link_path)
2085-
self.assert_mode_equal(0o666, st.st_mode)
2095+
mode = 0o644 if self.is_macos else 0o666
2096+
self.assert_mode_equal(mode, st.st_mode)
20862097
st = self.os.stat(link_path, follow_symlinks=False)
20872098
self.assert_mode_equal(0o6543, st.st_mode)
20882099

@@ -2097,7 +2108,7 @@ def test_lchmod(self):
20972108
self.os.lchmod(link_path, 0o6543)
20982109

20992110
st = self.os.stat(link_path)
2100-
self.assert_mode_equal(0o666, st.st_mode)
2111+
self.assert_mode_equal(0o644, st.st_mode)
21012112
st = self.os.lstat(link_path)
21022113
self.assert_mode_equal(0o6543, st.st_mode)
21032114

@@ -2807,9 +2818,9 @@ def test_nlink_for_directories(self):
28072818

28082819
def test_umask(self):
28092820
self.check_posix_only()
2810-
umask = os.umask(0o22)
2811-
os.umask(umask)
2812-
self.assertEqual(umask, self.os.umask(0o22))
2821+
umask = self.os.umask(0o22)
2822+
self.assertEqual(umask, self.os.umask(0o12))
2823+
self.os.umask(umask)
28132824

28142825
def test_mkdir_umask_applied(self):
28152826
"""mkdir creates a directory with umask applied."""
@@ -2826,7 +2837,7 @@ def test_mkdir_umask_applied(self):
28262837
def test_makedirs_umask_applied(self):
28272838
"""makedirs creates a directories with umask applied."""
28282839
self.check_posix_only()
2829-
self.os.umask(0o22)
2840+
umask = self.os.umask(0o22)
28302841
self.os.makedirs(self.make_path("p1", "dir1"))
28312842
self.assert_mode_equal(0o755, self.os.stat(self.make_path("p1")).st_mode)
28322843
self.assert_mode_equal(
@@ -2838,6 +2849,7 @@ def test_makedirs_umask_applied(self):
28382849
self.assert_mode_equal(
28392850
0o710, self.os.stat(self.make_path("p2", "dir2")).st_mode
28402851
)
2852+
self.os.umask(umask)
28412853

28422854
def test_mknod_umask_applied(self):
28432855
"""mkdir creates a device with umask applied."""
@@ -2998,7 +3010,7 @@ def use_real_fs(self):
29983010

29993011
class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
30003012
def setUp(self):
3001-
super(FakeOsModuleTestCaseInsensitiveFS, self).setUp()
3013+
super().setUp()
30023014
self.check_case_insensitive_fs()
30033015
self.rwx = self.os.R_OK | self.os.W_OK | self.os.X_OK
30043016
self.rw = self.os.R_OK | self.os.W_OK
@@ -3072,7 +3084,7 @@ def test_listdir_impossible_without_read_permission(self):
30723084
with self.assertRaises(PermissionError):
30733085
self.os.scandir(directory)
30743086
# we can access the file if we know the file name
3075-
assert self.os.stat(file_path).st_mode & 0o777 == 0o777
3087+
assert self.os.stat(file_path).st_mode & 0o777 == 0o755
30763088
with self.open(file_path) as f:
30773089
assert f.read() == "hey"
30783090

@@ -4109,10 +4121,6 @@ def test_utime_uses_open_fd_as_path(self):
41094121
class FakeOsModuleLowLevelFileOpTest(FakeOsModuleTestBase):
41104122
"""Test low level functions `os.open()`, `os.read()` and `os.write()`."""
41114123

4112-
def setUp(self):
4113-
os.umask(0o022)
4114-
super(FakeOsModuleLowLevelFileOpTest, self).setUp()
4115-
41164124
def test_open_read_only(self):
41174125
file_path = self.make_path("file1")
41184126
self.create_file(file_path, contents=b"contents")
@@ -4830,7 +4838,7 @@ def use_real_fs(self):
48304838

48314839
class FakeOsModuleDirFdTest(FakeOsModuleTestBase):
48324840
def setUp(self):
4833-
super(FakeOsModuleDirFdTest, self).setUp()
4841+
super().setUp()
48344842
self.check_posix_only()
48354843
if not self.use_real_fs():
48364844
# in the real OS, we test the option as is, in the fake OS
@@ -4948,11 +4956,11 @@ def os_stat():
49484956
os_stat()
49494957
self.add_supported_function(self.os.stat)
49504958
if self.os.stat in self.os.supports_dir_fd:
4951-
self.assertEqual(os_stat().st_mode, 0o100666)
4959+
self.assertEqual(os_stat().st_mode, 0o100644)
49524960

49534961
def test_lstat(self):
49544962
st = self.os.lstat(self.fname, dir_fd=self.dir_fd)
4955-
self.assertEqual(st.st_mode, 0o100666)
4963+
self.assertEqual(st.st_mode, 0o100644)
49564964

49574965
def test_mkdir(self):
49584966
def os_mkdir():
@@ -5185,7 +5193,7 @@ class FakeScandirTest(FakeOsModuleTestBase):
51855193
LINKED_FILE_SIZE = 10
51865194

51875195
def setUp(self):
5188-
super(FakeScandirTest, self).setUp()
5196+
super().setUp()
51895197
self.supports_symlinks = not self.is_windows or not self.use_real_fs()
51905198

51915199
if use_scandir_package:
@@ -5484,7 +5492,7 @@ def use_real_fs(self):
54845492
class FakeScandirFdTest(FakeScandirTest):
54855493
def tearDown(self):
54865494
self.os.close(self.dir_fd)
5487-
super(FakeScandirFdTest, self).tearDown()
5495+
super().tearDown()
54885496

54895497
def scandir_path(self):
54905498
# When scandir is called with a filedescriptor, only the name of the
@@ -5514,7 +5522,7 @@ def use_real_fs(self):
55145522

55155523
class FakeExtendedAttributeTest(FakeOsModuleTestBase):
55165524
def setUp(self):
5517-
super(FakeExtendedAttributeTest, self).setUp()
5525+
super().setUp()
55185526
self.check_linux_only()
55195527
self.dir_path = self.make_path("foo")
55205528
self.file_path = self.os.path.join(self.dir_path, "bar")
@@ -5570,7 +5578,7 @@ def setUp(self):
55705578
# and cannot be created in the real OS using file system
55715579
# functions only
55725580
self.check_posix_only()
5573-
super(FakeOsUnreadableDirTest, self).setUp()
5581+
super().setUp()
55745582
self.dir_path = self.make_path("some_dir")
55755583
self.file_path = self.os.path.join(self.dir_path, "some_file")
55765584
self.create_file(self.file_path)
@@ -5618,7 +5626,7 @@ def test_listdir_user_readable_dir_from_other_user(self):
56185626
self.check_posix_only()
56195627
user_id = get_uid()
56205628
set_uid(user_id + 1)
5621-
dir_path = self.make_path("dir1")
5629+
dir_path = "/dir1"
56225630
self.create_dir(dir_path, perm=0o600)
56235631
self.assertTrue(self.os.path.exists(dir_path))
56245632
reset_ids()
@@ -5630,8 +5638,9 @@ def test_listdir_user_readable_dir_from_other_user(self):
56305638

56315639
def test_listdir_group_readable_dir_from_other_user(self):
56325640
self.skip_real_fs() # won't change user in real fs
5641+
self.check_posix_only()
56335642
set_uid(get_uid() + 1)
5634-
dir_path = self.make_path("dir1")
5643+
dir_path = "/dir1"
56355644
self.create_dir(dir_path, perm=0o660)
56365645
self.assertTrue(self.os.path.exists(dir_path))
56375646
reset_ids()
@@ -5642,7 +5651,7 @@ def test_listdir_group_readable_dir_from_other_group(self):
56425651
self.check_posix_only()
56435652
group_id = self.os.getgid()
56445653
set_gid(group_id + 1)
5645-
dir_path = self.make_path("dir1")
5654+
dir_path = "/dir1"
56465655
self.create_dir(dir_path, perm=0o060)
56475656
self.assertTrue(self.os.path.exists(dir_path))
56485657
set_gid(group_id)
@@ -5687,9 +5696,10 @@ def test_remove_unreadable_dir(self):
56875696
self.assertFalse(self.os.path.exists(dir_path))
56885697

56895698
def test_remove_unreadable_dir_from_other_user(self):
5699+
self.check_posix_only()
56905700
self.skip_real_fs() # won't change user in real fs
56915701
set_uid(get_uid() + 1)
5692-
dir_path = self.make_path("dir1")
5702+
dir_path = "/dir1"
56935703
self.create_dir(dir_path, perm=0o000)
56945704
self.assertTrue(self.os.path.exists(dir_path))
56955705
reset_ids()

pyfakefs/tests/fake_pathlib_test.py

+12-7
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ def use_real_fs(self):
286286
class FakePathlibFileObjectPropertyTest(RealPathlibTestCase):
287287
def setUp(self):
288288
super(FakePathlibFileObjectPropertyTest, self).setUp()
289+
self.umask = self.os.umask(0o022)
289290
self.file_path = self.make_path("home", "jane", "test.py")
290291
self.create_file(self.file_path, contents=b"a" * 100)
291292
self.create_dir(self.make_path("home", "john"))
@@ -304,6 +305,9 @@ def setUp(self):
304305
self.make_path("home", "none", "test.py"),
305306
)
306307

308+
def tearDown(self):
309+
self.os.umask(self.umask)
310+
307311
def test_exists(self):
308312
self.skip_if_symlink_not_supported()
309313
self.assertTrue(self.path(self.file_path).exists())
@@ -373,14 +377,15 @@ def test_lstat_windows(self):
373377
self.skip_if_symlink_not_supported()
374378
self.check_lstat(0)
375379

376-
@unittest.skipIf(is_windows, "Linux specific behavior")
380+
@unittest.skipIf(is_windows, "POSIX specific behavior")
377381
def test_chmod(self):
378-
self.check_linux_only()
382+
self.check_posix_only()
379383
file_stat = self.os.stat(self.file_path)
380-
self.assertEqual(file_stat.st_mode, stat.S_IFREG | 0o666)
384+
self.assertEqual(file_stat.st_mode, stat.S_IFREG | 0o644)
381385
link_stat = self.os.lstat(self.file_link_path)
382386
# we get stat.S_IFLNK | 0o755 under MacOs
383-
self.assertEqual(link_stat.st_mode, stat.S_IFLNK | 0o777)
387+
mode = 0o755 if self.is_macos else 0o777
388+
self.assertEqual(link_stat.st_mode, stat.S_IFLNK | mode)
384389

385390
def test_lchmod(self):
386391
self.skip_if_symlink_not_supported()
@@ -391,7 +396,7 @@ def test_lchmod(self):
391396
self.path(self.file_link_path).lchmod(0o444)
392397
else:
393398
self.path(self.file_link_path).lchmod(0o444)
394-
self.assertEqual(file_stat.st_mode, stat.S_IFREG | 0o666)
399+
self.assertEqual(file_stat.st_mode, stat.S_IFREG | 0o644)
395400
# the exact mode depends on OS and Python version
396401
self.assertEqual(link_stat.st_mode & 0o777700, stat.S_IFLNK | 0o700)
397402

@@ -408,7 +413,7 @@ def test_chmod_no_followsymlinks(self):
408413
self.path(self.file_link_path).chmod(0o444, follow_symlinks=False)
409414
else:
410415
self.path(self.file_link_path).chmod(0o444, follow_symlinks=False)
411-
self.assertEqual(file_stat.st_mode, stat.S_IFREG | 0o666)
416+
self.assertEqual(file_stat.st_mode, stat.S_IFREG | 0o644)
412417
# the exact mode depends on OS and Python version
413418
self.assertEqual(link_stat.st_mode & 0o777700, stat.S_IFLNK | 0o700)
414419

@@ -492,7 +497,7 @@ def test_iterdir_impossible_without_read_permission(self):
492497
# glob does not find the file
493498
assert len(list(directory.glob("*.txt"))) == 0
494499
# we can access the file if we know the file name
495-
assert file_path.stat().st_mode & 0o777 == 0o777
500+
assert file_path.stat().st_mode & 0o777 == 0o755
496501
assert file_path.read_text() == "hey"
497502

498503
def test_resolve_nonexisting_file(self):

pyfakefs/tests/test_utils.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ def make_path(self, *args):
312312
args = [to_string(arg) for arg in args]
313313
return self.os.path.join(self.base_path, *args)
314314

315-
def create_dir(self, dir_path, perm=0o777):
315+
def create_dir(self, dir_path, perm=0o777, apply_umask=True):
316316
"""Create the directory at `dir_path`, including subdirectories.
317317
`dir_path` shall be composed using `make_path()`.
318318
"""
@@ -332,9 +332,15 @@ def create_dir(self, dir_path, perm=0o777):
332332
existing_path = self.os.path.join(existing_path, component)
333333
self.os.mkdir(existing_path)
334334
self.os.chmod(existing_path, 0o777)
335+
if apply_umask:
336+
umask = self.os.umask(0o022)
337+
perm &= ~umask
338+
self.os.umask(umask)
335339
self.os.chmod(dir_path, perm)
336340

337-
def create_file(self, file_path, contents=None, encoding=None, perm=0o666):
341+
def create_file(
342+
self, file_path, contents=None, encoding=None, perm=0o666, apply_umask=True
343+
):
338344
"""Create the given file at `file_path` with optional contents,
339345
including subdirectories. `file_path` shall be composed using
340346
`make_path()`.
@@ -347,6 +353,10 @@ def create_file(self, file_path, contents=None, encoding=None, perm=0o666):
347353
with self.open(file_path, mode) as f:
348354
if contents is not None:
349355
f.write(contents)
356+
if apply_umask:
357+
umask = self.os.umask(0o022)
358+
perm &= ~umask
359+
self.os.umask(umask)
350360
self.os.chmod(file_path, perm)
351361

352362
def create_symlink(self, link_path, target_path):

0 commit comments

Comments
 (0)