diff --git a/exdir/core/exdir_file.py b/exdir/core/exdir_file.py index ebca14c..4ba785c 100644 --- a/exdir/core/exdir_file.py +++ b/exdir/core/exdir_file.py @@ -115,14 +115,24 @@ def __init__(self, directory, mode=None, allow_remove=False, else: self.io_mode = OpenMode.READ_WRITE + parent_path = pathlib.PurePosixPath("") super().__init__( root_directory=directory, - parent_path=pathlib.PurePosixPath(""), + parent_path=parent_path, object_name="", file=self ) - already_exists = directory.exists() + # If we have name validation, we need to check for uniqueness. The + # directory may exist but with a different case, in which case we + # don't want to say that is already exists so that it matches the + # correct checks for the requested mode. + if name_validation != validation.none: + already_exists = validation.path_already_exists_case_insensitive( + directory.parent, directory.name.lower() + ) + else: + already_exists = directory.exists() if already_exists: if not exob.is_nonraw_object_directory(directory): raise RuntimeError( diff --git a/exdir/core/validation.py b/exdir/core/validation.py index 2b4b850..d47fa3f 100644 --- a/exdir/core/validation.py +++ b/exdir/core/validation.py @@ -1,7 +1,7 @@ from enum import Enum -import os +import os.path import sys -from pathlib import Path, WindowsPath +from tempfile import NamedTemporaryFile from unicodedata import category from . import constants as exob @@ -36,13 +36,14 @@ def _contains_control_character(s): def _assert_unique(parent_path, name): try: - name_str = str(name) + name = str(name) except UnicodeEncodeError: name = name.encode('utf8') - if (parent_path / name).exists(): + if path_already_exists_case_insensitive(parent_path, name.lower()): raise RuntimeError( - "'{}' already exists in '{}'".format(name, parent_path) + "A directory with name (case independent) '{}' already exists " + " and cannot be made according to the naming rule 'thorough'.".format(name) ) @@ -108,6 +109,7 @@ def strict(parent_path, name): _assert_unique(parent_path, name) _assert_valid_characters(name) + def thorough(parent_path, name): _assert_nonempty(parent_path, name) _assert_nonreserved(name) @@ -117,21 +119,16 @@ def thorough(parent_path, name): name_str = name.encode('utf8') name_lower = name_str.lower() _assert_valid_characters(name_lower) + _assert_unique(parent_path, name) + + +def none(parent_path, name): + pass - if isinstance(Path(parent_path), WindowsPath): - # use _assert_unique if we're already on Windows, because it is much faster - # than the test below - _assert_unique(parent_path, name) - return +def path_already_exists_case_insensitive(parent_path, name_lower): # os.listdir is much faster here than os.walk or parent_path.iterdir for item in os.listdir(str(parent_path)): if name_lower == item.lower(): - raise RuntimeError( - "A directory with name (case independent) '{}' already exists " - " and cannot be made according to the naming rule 'thorough'.".format(name) - ) - - -def none(parent_path, name): - pass + return True + return False diff --git a/tests/test_file.py b/tests/test_file.py index 12d80d6..82d2e92 100644 --- a/tests/test_file.py +++ b/tests/test_file.py @@ -187,6 +187,7 @@ def test_validate_name_thorough(setup_teardown_folder): f.close() with pytest.raises(RuntimeError): + # breakpoint() File(setup_teardown_folder[0] / "Test.exdir", name_validation=fv.thorough) with pytest.raises(NameError): File(setup_teardown_folder[0] / "tes#.exdir", name_validation=fv.thorough)