From 7d44ef0820bec6cdd36c97e57828d61d91cbb961 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Fri, 9 Jan 2026 01:04:42 +0000 Subject: [PATCH] Use `stat.S_ISDIR` for local folder scanning instead of `Path.is_dir` `Path.is_dir` on Python 3.14 now returns False for folders without search permissions: https://github.com/python/cpython/pull/118243 Therefore, the logic in the catch block will be skipped. --- b2sdk/_internal/scan/folder.py | 8 +++++++- changelog.d/+py314-test.fixed.md | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 changelog.d/+py314-test.fixed.md diff --git a/b2sdk/_internal/scan/folder.py b/b2sdk/_internal/scan/folder.py index 38dda43f..54b3ca2f 100644 --- a/b2sdk/_internal/scan/folder.py +++ b/b2sdk/_internal/scan/folder.py @@ -13,6 +13,7 @@ import os import platform import re +import stat import sys from abc import ABCMeta, abstractmethod from pathlib import Path @@ -274,14 +275,19 @@ def _walk_relative_paths( reporter.invalid_name(str(local_path), str(e)) continue + # Deliberately don't use Path.is_dir here: for directories + # without search permission, Python 3.13 raises PermissionError + # while Python 3.14 returns False. try: - is_dir = local_path.is_dir() + is_dir = stat.S_ISDIR(local_path.stat().st_mode) except PermissionError: # `chmod -x dir` can trigger this if reporter is not None and not policies_manager.should_exclude_local_directory( str(relative_file_path) ): reporter.local_permission_error(str(local_path)) continue + except (OSError, ValueError): + is_dir = False if is_dir: if policies_manager.should_exclude_local_directory(str(relative_file_path)): diff --git a/changelog.d/+py314-test.fixed.md b/changelog.d/+py314-test.fixed.md new file mode 100644 index 00000000..c95f5098 --- /dev/null +++ b/changelog.d/+py314-test.fixed.md @@ -0,0 +1 @@ +Use `stat.S_ISDIR` check for local folder children scanning instead of `Path.is_dir` to account for an api change in Python 3.14.