Skip to content

Commit ef6c1ea

Browse files
committed
Fix os.access behavior for symlinks under Windows
- found after enabling symlink tests for Windows
1 parent 1ef84cf commit ef6c1ea

File tree

9 files changed

+193
-170
lines changed

9 files changed

+193
-170
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ The released versions correspond to PyPI releases.
2222
(see [#1120](../../issues/1120))
2323
* fixed a regression that could break tests under Posix in Python 3.12
2424
(see [#1126](../../issues/1126))
25+
* fixed behavior for `os.access` for symlinks under Windows
2526

2627
## [Version 5.7.4](https://pypi.python.org/pypi/pyfakefs/5.7.4) (2025-01-14)
2728
Minor bugfix release.

pyfakefs/fake_os.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,8 +1021,8 @@ def access(
10211021
mode: int,
10221022
*,
10231023
dir_fd: Optional[int] = None,
1024-
effective_ids: bool = False,
1025-
follow_symlinks: bool = True,
1024+
effective_ids: Optional[bool] = None,
1025+
follow_symlinks: Optional[bool] = None,
10261026
) -> bool:
10271027
"""Check if a file exists and has the specified permissions.
10281028
@@ -1039,10 +1039,17 @@ def access(
10391039
Returns:
10401040
bool, `True` if file is accessible, `False` otherwise.
10411041
"""
1042-
if effective_ids and self.filesystem.is_windows_fs:
1042+
if effective_ids is not None and self.filesystem.is_windows_fs:
10431043
raise NotImplementedError(
10441044
"access: effective_ids unavailable on this platform"
10451045
)
1046+
if follow_symlinks is None:
1047+
# different behavior under Windows
1048+
follow_symlinks = not self.filesystem.is_windows_fs
1049+
elif self.filesystem.is_windows_fs:
1050+
raise NotImplementedError(
1051+
"access: follow_symlinks unavailable on this platform"
1052+
)
10461053
path = self._path_with_dir_fd(path, self.access, dir_fd)
10471054
try:
10481055
stat_result = self.stat(path, follow_symlinks=follow_symlinks)

pyfakefs/tests/fake_filesystem_shutil_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
from pyfakefs import fake_filesystem_unittest
2929
from pyfakefs.helpers import get_uid, set_uid, is_root, IS_PYPY
30-
from pyfakefs.tests.test_utils import RealFsTestMixin
30+
from pyfakefs.tests.test_utils import RealFsTestMixin, skip_if_symlink_not_supported
3131

3232
is_windows = sys.platform == "win32"
3333

@@ -244,7 +244,7 @@ def test_copystat(self):
244244
@unittest.skipIf(IS_PYPY, "Functionality not supported in PyPy")
245245
def test_copystat_symlinks(self):
246246
"""Regression test for #799"""
247-
self.skip_if_symlink_not_supported()
247+
skip_if_symlink_not_supported()
248248
f = self.make_path("xyzzy")
249249
self.create_file(f)
250250
sym1 = self.make_path("sym1")
@@ -434,7 +434,7 @@ def test_raises_if_source_and_dest_are_the_same_file(self):
434434
shutil.copyfile(src_file, dst_file)
435435

436436
def test_raises_if_dest_is_a_symlink_to_src(self):
437-
self.skip_if_symlink_not_supported()
437+
skip_if_symlink_not_supported()
438438
src_file = self.make_path("foo")
439439
dst_file = self.make_path("bar")
440440
contents = "contents of file"

pyfakefs/tests/fake_filesystem_test.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@
3434
OSType,
3535
)
3636
from pyfakefs.helpers import IS_WIN
37-
from pyfakefs.tests.test_utils import TestCase, RealFsTestCase, time_mock
37+
from pyfakefs.tests.test_utils import (
38+
TestCase,
39+
RealFsTestCase,
40+
time_mock,
41+
skip_if_symlink_not_supported,
42+
)
3843

3944

4045
class FakeDirectoryUnitTest(TestCase):
@@ -2039,7 +2044,7 @@ def test_that_unc_paths_are_auto_mounted(self):
20392044

20402045
class ConvenienceMethodTest(RealFsTestCase):
20412046
def test_create_link_with_non_existent_parent(self):
2042-
self.skip_if_symlink_not_supported()
2047+
skip_if_symlink_not_supported()
20432048
file1_path = self.make_path("test_file1")
20442049
link_path = self.make_path("nonexistent", "test_file2")
20452050

@@ -2050,7 +2055,7 @@ def test_create_link_with_non_existent_parent(self):
20502055
self.assertTrue(self.filesystem.exists(link_path))
20512056

20522057
def test_create_symlink_with_non_existent_parent(self):
2053-
self.skip_if_symlink_not_supported()
2058+
skip_if_symlink_not_supported()
20542059
file1_path = self.make_path("test_file1")
20552060
link_path = self.make_path("nonexistent", "test_file2")
20562061

@@ -2163,7 +2168,7 @@ def test_cannot_overwrite_symlink_with_dir(self):
21632168
self.filesystem.add_real_directory(root_dir, target_path="/root/")
21642169

21652170
def test_symlink_is_merged(self):
2166-
self.skip_if_symlink_not_supported()
2171+
skip_if_symlink_not_supported()
21672172
self.filesystem.create_dir(os.path.join("/", "root", "foo"))
21682173
with self.create_real_paths() as root_dir:
21692174
link_path = os.path.join(root_dir, "link.txt")
@@ -2428,7 +2433,7 @@ def test_add_existing_real_directory_symlink(self):
24282433
)
24292434

24302435
def test_add_existing_real_directory_symlink_target_path(self):
2431-
self.skip_if_symlink_not_supported()
2436+
skip_if_symlink_not_supported()
24322437
real_directory = self._setup_temp_directory()
24332438
symlinks = [
24342439
(
@@ -2453,7 +2458,7 @@ def test_add_existing_real_directory_symlink_target_path(self):
24532458
self.assertTrue(self.filesystem.exists("/path/fixtures/symlink_file_relative"))
24542459

24552460
def test_add_existing_real_directory_symlink_lazy_read(self):
2456-
self.skip_if_symlink_not_supported()
2461+
skip_if_symlink_not_supported()
24572462
real_directory = self._setup_temp_directory()
24582463
symlinks = [
24592464
(

pyfakefs/tests/fake_filesystem_vs_real_test.py

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import unittest
2424

2525
from pyfakefs import fake_filesystem, fake_os, fake_open
26-
from pyfakefs.helpers import IS_PYPY
26+
from pyfakefs.tests.test_utils import skip_if_symlink_not_supported
2727

2828

2929
def sep(path):
@@ -81,6 +81,7 @@ def _create_test_file(self, file_type, path, contents=None):
8181
fh.close()
8282
# l for symlink, h for hard link
8383
if file_type in ("l", "h"):
84+
contents = sep(contents)
8485
real_target, fake_target = (contents, contents)
8586
# If it begins with '/', make it relative to the base. You can't go
8687
# creating files in / for the real file system.
@@ -375,13 +376,16 @@ def assertOsPathMethodBehaviorMatches(
375376

376377
def assertAllOsBehaviorsMatch(self, path):
377378
path = sep(path)
378-
os_method_names = [] if self.is_windows else ["readlink"]
379+
os_method_names = ["readlink"]
379380
os_method_names_no_args = ["getcwd"]
380-
os_path_method_names = ["isabs", "isdir"]
381-
if not self.is_windows:
382-
os_path_method_names += ["islink", "lexists"]
383-
if not self.is_windows or not IS_PYPY:
384-
os_path_method_names += ["isfile", "exists"]
381+
os_path_method_names = [
382+
"isabs",
383+
"isdir",
384+
"islink",
385+
"lexists",
386+
"isfile",
387+
"exists",
388+
]
385389

386390
wrapped_methods = [
387391
["access", self._access_real, self._access_fake],
@@ -557,34 +561,33 @@ def test_file_with_binary_contents(self):
557561
self._create_test_file("b", "aFile", b"some contents")
558562
self.assertAllOsBehaviorsMatch("aFile")
559563

560-
@unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
561564
def test_sym_link_to_empty_file(self):
565+
skip_if_symlink_not_supported()
562566
self._create_test_file("f", "aFile")
563567
self._create_test_file("l", "link_to_empty", "aFile")
564568
self.assertAllOsBehaviorsMatch("link_to_empty")
565569

566-
@unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
567570
def test_hard_link_to_empty_file(self):
571+
skip_if_symlink_not_supported()
568572
self._create_test_file("f", "aFile")
569573
self._create_test_file("h", "link_to_empty", "aFile")
570574
self.assertAllOsBehaviorsMatch("link_to_empty")
571575

572-
@unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
573576
def test_sym_link_to_real_file(self):
577+
skip_if_symlink_not_supported()
574578
self._create_test_file("f", "aFile", "some contents")
575579
self._create_test_file("l", "link_to_file", "aFile")
576580
self.assertAllOsBehaviorsMatch("link_to_file")
577581

578-
@unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
579582
def test_hard_link_to_real_file(self):
583+
skip_if_symlink_not_supported()
580584
self._create_test_file("f", "aFile", "some contents")
581585
self._create_test_file("h", "link_to_file", "aFile")
582586
self.assertAllOsBehaviorsMatch("link_to_file")
583587

584-
@unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
585588
def test_broken_sym_link(self):
589+
skip_if_symlink_not_supported()
586590
self._create_test_file("l", "broken_link", "broken")
587-
self._create_test_file("l", "loop", "/a/loop")
588591
self.assertAllOsBehaviorsMatch("broken_link")
589592

590593
def test_file_in_a_folder(self):
@@ -593,16 +596,16 @@ def test_file_in_a_folder(self):
593596
self._create_test_file("f", "a/b/file", "contents")
594597
self.assertAllOsBehaviorsMatch("a/b/file")
595598

596-
@unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
597599
def test_absolute_sym_link_to_folder(self):
600+
skip_if_symlink_not_supported()
598601
self._create_test_file("d", "a")
599602
self._create_test_file("d", "a/b")
600603
self._create_test_file("f", "a/b/file", "contents")
601604
self._create_test_file("l", "a/link", "/a/b")
602605
self.assertAllOsBehaviorsMatch("a/link/file")
603606

604-
@unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
605607
def test_link_to_folder_after_chdir(self):
608+
skip_if_symlink_not_supported()
606609
self._create_test_file("d", "a")
607610
self._create_test_file("d", "a/b")
608611
self._create_test_file("f", "a/b/file", "contents")
@@ -613,51 +616,51 @@ def test_link_to_folder_after_chdir(self):
613616
self.fake_os.chdir(fake_dir)
614617
self.assertAllOsBehaviorsMatch("file")
615618

616-
@unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
617619
def test_relative_sym_link_to_folder(self):
620+
skip_if_symlink_not_supported()
618621
self._create_test_file("d", "a")
619622
self._create_test_file("d", "a/b")
620623
self._create_test_file("f", "a/b/file", "contents")
621624
self._create_test_file("l", "a/link", "b")
622625
self.assertAllOsBehaviorsMatch("a/link/file")
623626

624-
@unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
625627
def test_sym_link_to_parent(self):
628+
skip_if_symlink_not_supported()
626629
# Soft links on HFS+ / OS X behave differently.
627-
if os.uname()[0] != "Darwin":
630+
if sys.platform != "darwin":
628631
self._create_test_file("d", "a")
629632
self._create_test_file("d", "a/b")
630633
self._create_test_file("l", "a/b/c", "..")
631634
self.assertAllOsBehaviorsMatch("a/b/c")
632635

633-
@unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
634636
def test_path_through_sym_link_to_parent(self):
637+
skip_if_symlink_not_supported()
635638
self._create_test_file("d", "a")
636639
self._create_test_file("f", "a/target", "contents")
637640
self._create_test_file("d", "a/b")
638641
self._create_test_file("l", "a/b/c", "..")
639642
self.assertAllOsBehaviorsMatch("a/b/c/target")
640643

641-
@unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
642644
def test_sym_link_to_sibling_directory(self):
645+
skip_if_symlink_not_supported()
643646
self._create_test_file("d", "a")
644647
self._create_test_file("d", "a/b")
645648
self._create_test_file("d", "a/sibling_of_b")
646649
self._create_test_file("f", "a/sibling_of_b/target", "contents")
647650
self._create_test_file("l", "a/b/c", "../sibling_of_b")
648651
self.assertAllOsBehaviorsMatch("a/b/c/target")
649652

650-
@unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
651-
def test_sym_link_to_sibling_directory_non_existant_file(self):
653+
def test_sym_link_to_sibling_directory_non_existent_file(self):
654+
skip_if_symlink_not_supported()
652655
self._create_test_file("d", "a")
653656
self._create_test_file("d", "a/b")
654657
self._create_test_file("d", "a/sibling_of_b")
655658
self._create_test_file("f", "a/sibling_of_b/target", "contents")
656659
self._create_test_file("l", "a/b/c", "../sibling_of_b")
657660
self.assertAllOsBehaviorsMatch("a/b/c/file_does_not_exist")
658661

659-
@unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
660662
def test_broken_sym_link_to_sibling_directory(self):
663+
skip_if_symlink_not_supported()
661664
self._create_test_file("d", "a")
662665
self._create_test_file("d", "a/b")
663666
self._create_test_file("d", "a/sibling_of_b")

0 commit comments

Comments
 (0)