From 1a34c7826193281b74151b6f2024498bbab8d421 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sat, 18 Apr 2026 16:21:41 +0200 Subject: [PATCH] test and display features of used collection base directory --- radicale/pathutils.py | 75 +++++++++++++++++++- radicale/storage/multifilesystem/__init__.py | 3 + 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/radicale/pathutils.py b/radicale/pathutils.py index 36a66461..71ba4438 100644 --- a/radicale/pathutils.py +++ b/radicale/pathutils.py @@ -407,7 +407,7 @@ def path_supports_symlink(path): def path_is_collision_free_case_sensitive(path): - # Test: case sensitive + """Check whether path supports case sensitive entries.""" if not os.path.isdir(path): raise ValueError("%r is not a path" % (path)) base_dir = tempfile.mkdtemp(dir=path) @@ -452,3 +452,76 @@ def path_is_collision_free_no_short_filename(path): os.rmdir(base_dir) logger.debug("path_is_collision_free (no short-filename): path=%r result=%s", path, result) return result + + +def path_supports_unicode(path): + """Check whether path supports unicode.""" + if not os.path.isdir(path): + raise ValueError("%r is not a path" % (path)) + base_dir = tempfile.mkdtemp(dir=path) + part = "TEST😀" + test_dir = os.path.join(base_dir, part) + result = True + try: + os.mkdir(test_dir) + except OSError: + result = False + else: + with os.scandir(base_dir) as entries: + if part not in (e.name for e in entries): + result = False + # cleanup + os.rmdir(test_dir) + # cleanup + os.rmdir(base_dir) + logger.debug("path_supports_unicode: path=%r result=%s", path, result) + return result + + +def path_supports_trailing_whitespace(path): + """Check whether path supports trailing whitespace.""" + if not os.path.isdir(path): + raise ValueError("%r is not a path" % (path)) + base_dir = tempfile.mkdtemp(dir=path) + part = "TEST " + test_dir = os.path.join(base_dir, part) + result = True + try: + os.mkdir(test_dir) + except OSError: + result = False + else: + with os.scandir(base_dir) as entries: + if part not in (e.name for e in entries): + result = False + # cleanup + os.rmdir(test_dir) + # cleanup + os.rmdir(base_dir) + logger.debug("path_supports_trailing_whitespace: path=%r result=%s", path, result) + return result + + +def path_supports_problematic_chars(path): + """Check whether path supports problematic chars.""" + if not os.path.isdir(path): + raise ValueError("%r is not a path" % (path)) + base_dir = tempfile.mkdtemp(dir=path) + result = True + for char in ['*', '?']: + part = "TES" + char + "T" + test_dir = os.path.join(base_dir, part) + try: + os.mkdir(test_dir) + except OSError: + result = False + else: + with os.scandir(base_dir) as entries: + if part not in (e.name for e in entries): + result = False + # cleanup + os.rmdir(test_dir) + # cleanup + os.rmdir(base_dir) + logger.debug("path_supports_problematic chars: path=%r result=%s", path, result) + return result diff --git a/radicale/storage/multifilesystem/__init__.py b/radicale/storage/multifilesystem/__init__.py index 7e6ead8e..7deeaac7 100644 --- a/radicale/storage/multifilesystem/__init__.py +++ b/radicale/storage/multifilesystem/__init__.py @@ -180,6 +180,9 @@ class Storage( self._filesystem_root_folder_is_collision_free, filesystem_root_folder_is_collision_free_case_sensitive, filesystem_root_folder_is_collision_free_no_short_filename) + logger.info("Storage location subfolder suppports unicode: %s", pathutils.path_supports_unicode(self._get_collection_root_folder())) + logger.info("Storage location subfolder suppports trailing whitespace: %s", pathutils.path_supports_trailing_whitespace(self._get_collection_root_folder())) + logger.info("Storage location subfolder suppports problematic chars: %s", pathutils.path_supports_problematic_chars(self._get_collection_root_folder())) logger.info("Storage cache subfolder usage for 'item': %s", self._use_cache_subfolder_for_item) logger.info("Storage cache subfolder usage for 'history': %s", self._use_cache_subfolder_for_history) logger.info("Storage cache subfolder usage for 'sync-token': %s", self._use_cache_subfolder_for_synctoken)