Skip to content

Commit fadf4f8

Browse files
committed
Adapt error codes to match newer Windows 11 behavior
- for some OSError exceptions, errno has changed
1 parent f9380ce commit fadf4f8

File tree

4 files changed

+40
-78
lines changed

4 files changed

+40
-78
lines changed

CHANGES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ The released versions correspond to PyPI releases.
1515

1616
### Unreleased
1717

18+
## Changes
19+
* the `errno` codes set in `OSError` have changed for some specific error conditions
20+
in Windows 11/Windows Server 2025; pyfakefs now matches this behavior
21+
instead of the previous behavior under Windows 10
22+
1823
## Fixes
1924
* fixes patching of Debian-specific `tempfile` in Python 3.13 (see [#1214](../../issues/1214))
2025

pyfakefs/fake_filesystem.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ def raise_for_filepath_ending_with_separator(
770770
if self.is_macos and exc.errno != errno.ENOENT:
771771
return
772772
if self.is_windows_fs:
773-
self.raise_os_error(errno.EINVAL, entry_path)
773+
self.raise_os_error(errno.ENOTDIR, entry_path)
774774
raise
775775
if not follow_symlinks or self.is_windows_fs or self.is_macos:
776776
file_object = link_object
@@ -781,8 +781,7 @@ def raise_for_filepath_ending_with_separator(
781781
else:
782782
is_error = not S_ISDIR(file_object.st_mode)
783783
if is_error:
784-
error_nr = errno.EINVAL if self.is_windows_fs else errno.ENOTDIR
785-
self.raise_os_error(error_nr, entry_path)
784+
self.raise_os_error(errno.ENOTDIR, entry_path)
786785

787786
def chmod(
788787
self,
@@ -2004,7 +2003,7 @@ def rename(
20042003
self.raise_os_error(errno.EXDEV, old_path)
20052004
if not S_ISDIR(new_dir_object.st_mode):
20062005
self.raise_os_error(
2007-
errno.EACCES if self.is_windows_fs else errno.ENOTDIR, new_path
2006+
errno.EINVAL if self.is_windows_fs else errno.ENOTDIR, new_path
20082007
)
20092008
if new_dir_object.has_parent_object(old_object):
20102009
self.raise_os_error(errno.EINVAL, new_path)
@@ -2038,13 +2037,7 @@ def _handle_broken_link_with_trailing_sep(self, path: AnyStr) -> None:
20382037
# note that the check for trailing sep has to be done earlier
20392038
if self.islink(path):
20402039
if not self.exists(path):
2041-
error = (
2042-
errno.ENOENT
2043-
if self.is_macos
2044-
else errno.EINVAL
2045-
if self.is_windows_fs
2046-
else errno.ENOTDIR
2047-
)
2040+
error = errno.ENOENT if self.is_macos else errno.ENOTDIR
20482041
self.raise_os_error(error, path)
20492042

20502043
def _handle_posix_dir_link_errors(
@@ -2080,7 +2073,7 @@ def _rename_to_existing_path(
20802073
new_object = self._get_object(new_file_path)
20812074
if old_file_path == new_file_path:
20822075
if not S_ISLNK(new_object.st_mode) and ends_with_sep:
2083-
error = errno.EINVAL if self.is_windows_fs else errno.ENOTDIR
2076+
error = errno.ENOTDIR if self.is_windows_fs else errno.ENOTDIR
20842077
self.raise_os_error(error, old_file_path)
20852078
return None # Nothing to do here
20862079

@@ -2683,7 +2676,7 @@ def create_symlink(
26832676
self.raise_os_error(errno.ENOENT, link_path)
26842677
else:
26852678
if self.is_windows_fs:
2686-
self.raise_os_error(errno.EINVAL, link_target_path)
2679+
self.raise_os_error(errno.ENOTDIR, link_target_path)
26872680
if not self.exists(
26882681
self._path_without_trailing_separators(link_path),
26892682
check_link=True,
@@ -2754,8 +2747,7 @@ def create_link(
27542747
self.raise_os_error(errno.ENOENT, new_parent_directory)
27552748

27562749
if self.ends_with_path_separator(old_path_str):
2757-
error = errno.EINVAL if self.is_windows_fs else errno.ENOTDIR
2758-
self.raise_os_error(error, old_path_str)
2750+
self.raise_os_error(errno.ENOTDIR, old_path_str)
27592751

27602752
if not self.is_windows_fs and self.ends_with_path_separator(new_path):
27612753
self.raise_os_error(errno.ENOENT, old_path_str)
@@ -2839,7 +2831,7 @@ def readlink(self, path: AnyPath) -> str:
28392831
self.raise_os_error(errno.EINVAL, link_path)
28402832
if not self.exists(link_obj.path): # type: ignore
28412833
if self.is_windows_fs:
2842-
error = errno.EINVAL
2834+
error = errno.ENOTDIR
28432835
elif self._is_circular_link(link_obj):
28442836
if self.is_macos:
28452837
return link_obj.path # type: ignore[return-value]

pyfakefs/fake_path.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,7 @@ def getsize(self, path: AnyStr):
163163
self.filesystem.ends_with_path_separator(path)
164164
and S_IFMT(file_obj.st_mode) != S_IFDIR
165165
):
166-
error_nr = errno.EINVAL if self.filesystem.is_windows_fs else errno.ENOTDIR
167-
self.filesystem.raise_os_error(error_nr, path)
166+
self.filesystem.raise_os_error(errno.ENOTDIR, path)
168167
return file_obj.st_size
169168

170169
def isabs(self, path: AnyStr) -> bool:

pyfakefs/tests/fake_os_test.py

Lines changed: 26 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -521,21 +521,13 @@ def test_islink_with_trailing_sep_macos(self):
521521
self.os.symlink(self.base_path, link_path)
522522
self.assertFalse(self.os.path.islink(link_path + self.os.sep))
523523

524-
def check_getsize_raises_with_trailing_separator(self, error_nr):
524+
def test_getsize_raises_with_trailing_separator(self):
525525
file_path = self.make_path("bar")
526526
self.create_file(file_path)
527527
self.assert_raises_os_error(
528-
error_nr, self.os.path.getsize, file_path + self.os.sep
528+
errno.ENOTDIR, self.os.path.getsize, file_path + self.os.sep
529529
)
530530

531-
def test_getsize_raises_with_trailing_separator_posix(self):
532-
self.check_posix_only()
533-
self.check_getsize_raises_with_trailing_separator(errno.ENOTDIR)
534-
535-
def test_getsize_raises_with_trailing_separator_windows(self):
536-
self.check_windows_only()
537-
self.check_getsize_raises_with_trailing_separator(errno.EINVAL)
538-
539531
def check_remove_link_ending_with_sep(self, error_nr):
540532
# regression test for #360
541533
link_path = self.make_path("foo")
@@ -687,33 +679,21 @@ def test_isfile_not_readable_file(self):
687679
self.create_file(file_path, perm=0)
688680
self.assertTrue(self.os.path.isfile(file_path))
689681

690-
def check_stat_with_trailing_separator(self, error_nr):
682+
def test_stat_with_trailing_separator(self):
691683
# regression test for #376
692684
file_path = self.make_path("foo")
693685
self.create_file(file_path)
694-
self.assert_raises_os_error(error_nr, self.os.stat, file_path + self.os.sep)
695-
696-
def test_stat_with_trailing_separator_posix(self):
697-
self.check_posix_only()
698-
self.check_stat_with_trailing_separator(errno.ENOTDIR)
699-
700-
def test_stat_with_trailing_separator_windows(self):
701-
self.check_windows_only()
702-
self.check_stat_with_trailing_separator(errno.EINVAL)
686+
self.assert_raises_os_error(
687+
errno.ENOTDIR, self.os.stat, file_path + self.os.sep
688+
)
703689

704-
def check_remove_with_trailing_separator(self, error_nr):
690+
def test_remove_with_trailing_separator(self):
705691
# regression test for #377
706692
file_path = self.make_path("foo")
707693
self.create_file(file_path)
708-
self.assert_raises_os_error(error_nr, self.os.remove, file_path + self.os.sep)
709-
710-
def test_remove_with_trailing_separator_posix(self):
711-
self.check_posix_only()
712-
self.check_remove_with_trailing_separator(errno.ENOTDIR)
713-
714-
def test_remove_with_trailing_separator_windows(self):
715-
self.check_windows_only()
716-
self.check_remove_with_trailing_separator(errno.EINVAL)
694+
self.assert_raises_os_error(
695+
errno.ENOTDIR, self.os.remove, file_path + self.os.sep
696+
)
717697

718698
def test_readlink(self):
719699
skip_if_symlink_not_supported()
@@ -790,7 +770,7 @@ def test_broken_symlink_with_trailing_separator_windows(self):
790770
link_path = self.make_path("link")
791771
self.os.symlink(file_path, link_path)
792772
self.assert_raises_os_error(
793-
errno.EINVAL,
773+
errno.ENOTDIR,
794774
self.os.symlink,
795775
link_path + self.os.sep,
796776
link_path + self.os.sep,
@@ -819,7 +799,7 @@ def test_circular_readlink_with_trailing_separator_windows(self):
819799
file_path = self.make_path("foo")
820800
self.os.symlink(file_path, file_path)
821801
self.assert_raises_os_error(
822-
errno.EINVAL, self.os.readlink, file_path + self.os.sep
802+
errno.ENOTDIR, self.os.readlink, file_path + self.os.sep
823803
)
824804

825805
def test_readlink_with_links_in_path(self):
@@ -1125,7 +1105,7 @@ def test_rename_with_target_parent_file_raises_windows(self):
11251105
file_path = self.make_path("foo", "baz")
11261106
self.create_file(file_path)
11271107
self.assert_raises_os_error(
1128-
errno.EACCES,
1108+
errno.EINVAL,
11291109
self.os.rename,
11301110
file_path,
11311111
self.os.path.join(file_path, "new"),
@@ -2449,7 +2429,7 @@ def test_broken_symlink_with_trailing_sep_windows(self):
24492429
self.check_windows_only()
24502430
skip_if_symlink_not_supported()
24512431
path0 = self.make_path("foo") + self.os.sep
2452-
self.assert_raises_os_error(errno.EINVAL, self.os.symlink, path0, path0)
2432+
self.assert_raises_os_error(errno.ENOTDIR, self.os.symlink, path0, path0)
24532433

24542434
def test_rename_symlink_with_trailing_sep_linux(self):
24552435
# Regression test for #391
@@ -2509,7 +2489,7 @@ def test_lstat_broken_link_with_trailing_sep_macos(self):
25092489
def test_lstat_broken_link_with_trailing_sep_windows(self):
25102490
self.check_windows_only()
25112491
link_path = self.create_broken_link_path_with_trailing_sep()
2512-
self.assert_raises_os_error(errno.EINVAL, self.os.lstat, link_path)
2492+
self.assert_raises_os_error(errno.ENOTDIR, self.os.lstat, link_path)
25132493

25142494
def test_mkdir_broken_link_with_trailing_sep_linux_windows(self):
25152495
self.check_linux_and_windows()
@@ -2540,7 +2520,7 @@ def test_remove_broken_link_with_trailing_sep_macos(self):
25402520
def test_remove_broken_link_with_trailing_sep_windows(self):
25412521
self.check_windows_only()
25422522
link_path = self.create_broken_link_path_with_trailing_sep()
2543-
self.assert_raises_os_error(errno.EINVAL, self.os.remove, link_path)
2523+
self.assert_raises_os_error(errno.ENOTDIR, self.os.remove, link_path)
25442524

25452525
def test_rename_broken_link_with_trailing_sep_linux(self):
25462526
self.check_linux_only()
@@ -2560,7 +2540,7 @@ def test_rename_broken_link_with_trailing_sep_windows(self):
25602540
self.check_windows_only()
25612541
link_path = self.create_broken_link_path_with_trailing_sep()
25622542
self.assert_raises_os_error(
2563-
errno.EINVAL, self.os.rename, link_path, self.make_path("target")
2543+
errno.ENOTDIR, self.os.rename, link_path, self.make_path("target")
25642544
)
25652545

25662546
def test_readlink_broken_link_with_trailing_sep_posix(self):
@@ -2571,7 +2551,7 @@ def test_readlink_broken_link_with_trailing_sep_posix(self):
25712551
def test_readlink_broken_link_with_trailing_sep_windows(self):
25722552
self.check_windows_only()
25732553
link_path = self.create_broken_link_path_with_trailing_sep()
2574-
self.assert_raises_os_error(errno.EINVAL, self.os.readlink, link_path)
2554+
self.assert_raises_os_error(errno.ENOTDIR, self.os.readlink, link_path)
25752555

25762556
def test_islink_broken_link_with_trailing_sep(self):
25772557
link_path = self.create_broken_link_path_with_trailing_sep()
@@ -2618,24 +2598,16 @@ def test_open_broken_symlink_to_path_with_trailing_sep_windows(self):
26182598
self.check_windows_only()
26192599
self.check_open_broken_symlink_to_path_with_trailing_sep(errno.EINVAL)
26202600

2621-
def check_link_path_ending_with_sep(self, error):
2601+
def test_link_path_ending_with_sep(self):
26222602
# Regression tests for #399
26232603
skip_if_symlink_not_supported()
26242604
file_path = self.make_path("foo")
26252605
link_path = self.make_path("link")
26262606
with self.open(file_path, "w", encoding="utf8"):
26272607
self.assert_raises_os_error(
2628-
error, self.os.link, file_path + self.os.sep, link_path
2608+
errno.ENOTDIR, self.os.link, file_path + self.os.sep, link_path
26292609
)
26302610

2631-
def test_link_path_ending_with_sep_posix(self):
2632-
self.check_posix_only()
2633-
self.check_link_path_ending_with_sep(errno.ENOTDIR)
2634-
2635-
def test_link_path_ending_with_sep_windows(self):
2636-
self.check_windows_only()
2637-
self.check_link_path_ending_with_sep(errno.EINVAL)
2638-
26392611
def test_link_to_path_ending_with_sep_posix(self):
26402612
# regression test for #407
26412613
self.check_posix_only()
@@ -2653,22 +2625,14 @@ def test_link_to_path_ending_with_sep_windows(self):
26532625
self.os.link(path1, path0)
26542626
self.assertTrue(self.os.path.exists(path1))
26552627

2656-
def check_rename_to_path_ending_with_sep(self, error):
2628+
def test_rename_to_path_ending_with_sep(self):
26572629
# Regression tests for #400
26582630
file_path = self.make_path("foo")
26592631
with self.open(file_path, "w", encoding="utf8"):
26602632
self.assert_raises_os_error(
2661-
error, self.os.rename, file_path + self.os.sep, file_path
2633+
errno.ENOTDIR, self.os.rename, file_path + self.os.sep, file_path
26622634
)
26632635

2664-
def test_rename_to_path_ending_with_sep_posix(self):
2665-
self.check_posix_only()
2666-
self.check_rename_to_path_ending_with_sep(errno.ENOTDIR)
2667-
2668-
def test_rename_to_path_ending_with_sep_windows(self):
2669-
self.check_windows_only()
2670-
self.check_rename_to_path_ending_with_sep(errno.EINVAL)
2671-
26722636
def test_rmdir_link_with_trailing_sep_linux(self):
26732637
self.check_linux_only()
26742638
dir_path = self.make_path("foo")
@@ -2723,7 +2687,9 @@ def test_readlink_circular_link_with_trailing_sep_windows(self):
27232687
path0 = self.make_path("bar")
27242688
self.os.symlink(path0, path1)
27252689
self.os.symlink(path1, path0)
2726-
self.assert_raises_os_error(errno.EINVAL, self.os.readlink, path0 + self.os.sep)
2690+
self.assert_raises_os_error(
2691+
errno.ENOTDIR, self.os.readlink, path0 + self.os.sep
2692+
)
27272693

27282694
# hard link related tests
27292695
def test_link_bogus(self):
@@ -3510,7 +3476,7 @@ def test_rename_with_target_parent_file_raises_windows(self):
35103476
file_path = self.make_path("foo", "baz")
35113477
self.create_file(file_path)
35123478
self.assert_raises_os_error(
3513-
errno.EACCES,
3479+
errno.EINVAL,
35143480
self.os.rename,
35153481
file_path,
35163482
self.os.path.join(file_path.upper(), "new"),

0 commit comments

Comments
 (0)