diff --git a/pathvalidate/_base.py b/pathvalidate/_base.py index 20eeb18..dc42116 100644 --- a/pathvalidate/_base.py +++ b/pathvalidate/_base.py @@ -21,6 +21,7 @@ class BaseFile: _INVALID_WIN_FILENAME_CHARS: ClassVar[str] = ( _INVALID_FILENAME_CHARS + _INVALID_WIN_PATH_CHARS + "\\" ) + _DEFAULT_MAX_FILENAME_LEN = 255 @property def platform(self) -> Platform: @@ -32,14 +33,24 @@ def reserved_keywords(self) -> Tuple[str, ...]: @property def max_len(self) -> int: - return self._max_len + return self._max_filepath_len + + @property + def max_filename_len(self) -> int: + return self._max_filename_len + + @property + def max_filepath_len(self) -> int: + return self._max_filepath_len def __init__( self, - max_len: int, fs_encoding: Optional[str], + max_filename_len: Optional[int] = None, + max_filepath_len: Optional[int] = None, additional_reserved_names: Optional[Sequence[str]] = None, - platform_max_len: Optional[int] = None, + platform_max_filename_len: Optional[int] = None, + platform_max_filepath_len: Optional[int] = None, platform: Optional[PlatformType] = None, ) -> None: if additional_reserved_names is None: @@ -48,15 +59,25 @@ def __init__( self.__platform = normalize_platform(platform) - if platform_max_len is None: - platform_max_len = self._get_default_max_path_len() + # determine max filepath length + if platform_max_filepath_len is None: + platform_max_filepath_len = self._get_default_max_path_len() - if max_len <= 0: - self._max_len = platform_max_len + if max_filepath_len is None or max_filepath_len <= 0: + self._max_filepath_len = platform_max_filepath_len else: - self._max_len = max_len + self._max_filepath_len = min(max_filepath_len, platform_max_filepath_len) - self._max_len = min(self._max_len, platform_max_len) + # determine max filename length + if platform_max_filename_len is None: + platform_max_filename_len = self._get_default_max_name_len() + + if max_filename_len is None or max_filename_len <= 0: + self._max_filename_len = platform_max_filename_len + else: + self._max_filename_len = max_filename_len + # name cannot be longer than max path length + self._max_filename_len = min(self._max_filename_len, self._max_filepath_len) if fs_encoding: self._fs_encoding = fs_encoding @@ -99,24 +120,29 @@ def _get_default_max_path_len(self) -> int: return 260 # universal + def _get_default_max_name_len(self) -> int: + return self._DEFAULT_MAX_FILENAME_LEN + class AbstractValidator(BaseFile, metaclass=abc.ABCMeta): def __init__( self, - max_len: int, fs_encoding: Optional[str], check_reserved: bool, + max_filename_len: Optional[int] = None, + max_filepath_len: Optional[int] = None, additional_reserved_names: Optional[Sequence[str]] = None, - platform_max_len: Optional[int] = None, + platform_max_filepath_len: Optional[int] = None, platform: Optional[PlatformType] = None, ) -> None: self._check_reserved = check_reserved super().__init__( - max_len, fs_encoding, + max_filename_len=max_filename_len, + max_filepath_len=max_filepath_len, additional_reserved_names=additional_reserved_names, - platform_max_len=platform_max_len, + platform_max_filepath_len=platform_max_filepath_len, platform=platform, ) @@ -145,21 +171,23 @@ class AbstractSanitizer(BaseFile, metaclass=abc.ABCMeta): def __init__( self, validator: AbstractValidator, - max_len: int, fs_encoding: Optional[str], validate_after_sanitize: bool, + max_filename_len: Optional[int] = None, + max_filepath_len: Optional[int] = None, null_value_handler: Optional[ValidationErrorHandler] = None, reserved_name_handler: Optional[ValidationErrorHandler] = None, additional_reserved_names: Optional[Sequence[str]] = None, - platform_max_len: Optional[int] = None, + platform_max_filepath_len: Optional[int] = None, platform: Optional[PlatformType] = None, ) -> None: super().__init__( - max_len=max_len, fs_encoding=fs_encoding, additional_reserved_names=additional_reserved_names, - platform_max_len=platform_max_len, + platform_max_filepath_len=platform_max_filepath_len, platform=platform, + max_filename_len=max_filename_len, + max_filepath_len=max_filepath_len, ) if null_value_handler is None: @@ -187,11 +215,12 @@ def min_len(self) -> int: def __init__( self, min_len: int, - max_len: int, fs_encoding: Optional[str], check_reserved: bool, + max_filename_len: Optional[int] = None, + max_filepath_len: Optional[int] = None, additional_reserved_names: Optional[Sequence[str]] = None, - platform_max_len: Optional[int] = None, + platform_max_filepath_len: Optional[int] = None, platform: Optional[PlatformType] = None, ) -> None: if min_len <= 0: @@ -199,11 +228,12 @@ def __init__( self._min_len = max(min_len, 1) super().__init__( - max_len=max_len, fs_encoding=fs_encoding, + max_filename_len=max_filename_len, + max_filepath_len=max_filepath_len, check_reserved=check_reserved, additional_reserved_names=additional_reserved_names, - platform_max_len=platform_max_len, + platform_max_filepath_len=platform_max_filepath_len, platform=platform, ) @@ -227,11 +257,14 @@ def _validate_reserved_keywords(self, name: str) -> None: ) def _validate_max_len(self) -> None: - if self.max_len < 1: - raise ValueError("max_len must be greater or equal to one") + if self.max_filename_len < 1: + raise ValueError("max_filename_len must be greater or equal to one") + + if self.max_filepath_len < 1: + raise ValueError("max_filepath_len must be greater or equal to one") - if self.min_len > self.max_len: - raise ValueError("min_len must be lower than max_len") + if self.min_len > self.max_filename_len: + raise ValueError("min_len must be lower than max_filename_len") @staticmethod def __extract_root_name(path: str) -> str: diff --git a/pathvalidate/_filename.py b/pathvalidate/_filename.py index 1b2168f..0203a77 100644 --- a/pathvalidate/_filename.py +++ b/pathvalidate/_filename.py @@ -18,7 +18,6 @@ from .handler import ReservedNameHandler, ValidationErrorHandler -_DEFAULT_MAX_FILENAME_LEN = 255 _RE_INVALID_FILENAME = re.compile(f"[{re.escape(BaseFile._INVALID_FILENAME_CHARS):s}]", re.UNICODE) _RE_INVALID_WIN_FILENAME = re.compile( f"[{re.escape(BaseFile._INVALID_WIN_FILENAME_CHARS):s}]", re.UNICODE @@ -28,7 +27,7 @@ class FileNameSanitizer(AbstractSanitizer): def __init__( self, - max_len: int = _DEFAULT_MAX_FILENAME_LEN, + max_len: Optional[int] = None, fs_encoding: Optional[str] = None, platform: Optional[PlatformType] = None, null_value_handler: Optional[ValidationErrorHandler] = None, @@ -36,13 +35,16 @@ def __init__( additional_reserved_names: Optional[Sequence[str]] = None, validate_after_sanitize: bool = False, validator: Optional[AbstractValidator] = None, + max_filename_len: Optional[int] = None, ) -> None: + if max_len is not None: + max_filename_len = max_len if validator: fname_validator = validator else: fname_validator = FileNameValidator( min_len=DEFAULT_MIN_LEN, - max_len=max_len, + max_len=max_filename_len, fs_encoding=fs_encoding, check_reserved=True, additional_reserved_names=additional_reserved_names, @@ -50,12 +52,12 @@ def __init__( ) super().__init__( - max_len=max_len, fs_encoding=fs_encoding, + max_filename_len=max_filename_len, + max_filepath_len=max_filename_len, null_value_handler=null_value_handler, reserved_name_handler=reserved_name_handler, additional_reserved_names=additional_reserved_names, - platform_max_len=_DEFAULT_MAX_FILENAME_LEN, platform=platform, validate_after_sanitize=validate_after_sanitize, validator=fname_validator, @@ -75,7 +77,9 @@ def sanitize(self, value: PathType, replacement_text: str = "") -> PathType: raise sanitized_filename = self._sanitize_regexp.sub(replacement_text, str(value)) - sanitized_filename = truncate_str(sanitized_filename, self._fs_encoding, self.max_len) + sanitized_filename = truncate_str( + sanitized_filename, self._fs_encoding, self.max_filename_len + ) try: self._validator.validate(sanitized_filename) @@ -149,19 +153,22 @@ def reserved_keywords(self) -> Tuple[str, ...]: def __init__( self, min_len: int = DEFAULT_MIN_LEN, - max_len: int = _DEFAULT_MAX_FILENAME_LEN, + max_len: Optional[int] = None, fs_encoding: Optional[str] = None, platform: Optional[PlatformType] = None, check_reserved: bool = True, additional_reserved_names: Optional[Sequence[str]] = None, + max_filename_len: Optional[int] = None, ) -> None: + if max_len is not None: + max_filename_len = max_len super().__init__( min_len=min_len, - max_len=max_len, + max_filename_len=max_filename_len, + max_filepath_len=max_filename_len, fs_encoding=fs_encoding, check_reserved=check_reserved, additional_reserved_names=additional_reserved_names, - platform_max_len=_DEFAULT_MAX_FILENAME_LEN, platform=platform, ) @@ -179,10 +186,10 @@ def validate(self, value: PathType) -> None: ErrorAttrKey.FS_ENCODING: self._fs_encoding, ErrorAttrKey.BYTE_COUNT: byte_ct, } - if byte_ct > self.max_len: + if byte_ct > self.max_filename_len: raise ValidationError( [ - f"filename is too long: expected<={self.max_len:d} bytes, actual={byte_ct:d} bytes" + f"filename is too long: expected<={self.max_filename_len:d} bytes, actual={byte_ct:d} bytes" ], **err_kwargs, ) @@ -266,10 +273,11 @@ def validate_filename( filename: PathType, platform: Optional[PlatformType] = None, min_len: int = DEFAULT_MIN_LEN, - max_len: int = _DEFAULT_MAX_FILENAME_LEN, + max_len: Optional[int] = None, fs_encoding: Optional[str] = None, check_reserved: bool = True, additional_reserved_names: Optional[Sequence[str]] = None, + max_filename_len: Optional[int] = None, ) -> None: """Verifying whether the ``filename`` is a valid file name or not. @@ -284,14 +292,7 @@ def validate_filename( Minimum byte length of the ``filename``. The value must be greater or equal to one. Defaults to ``1``. max_len: - Maximum byte length of the ``filename``. The value must be lower than: - - - ``Linux``: 4096 - - ``macOS``: 1024 - - ``Windows``: 260 - - ``universal``: 260 - - Defaults to ``255``. + Alias for ``max_filename_len``. fs_encoding: Filesystem encoding that used to calculate the byte length of the filename. If |None|, get the value from the execution environment. @@ -300,10 +301,13 @@ def validate_filename( additional_reserved_names: Additional reserved names to check. Case insensitive. + max_filename_len: + Maximum byte length of the ``filename``. + Defaults to ``255``. Raises: ValidationError (ErrorReason.INVALID_LENGTH): - If the ``filename`` is longer than ``max_len`` characters. + If the ``filename`` is longer than ``max_filename_len`` characters. ValidationError (ErrorReason.INVALID_CHARACTER): If the ``filename`` includes invalid character(s) for a filename: |invalid_filename_chars|. @@ -321,11 +325,12 @@ def validate_filename( `Naming Files, Paths, and Namespaces - Win32 apps | Microsoft Docs `__ """ - + if max_len is not None: + max_filename_len = max_len FileNameValidator( platform=platform, min_len=min_len, - max_len=max_len, + max_filename_len=max_filename_len, fs_encoding=fs_encoding, check_reserved=check_reserved, additional_reserved_names=additional_reserved_names, @@ -340,6 +345,7 @@ def is_valid_filename( fs_encoding: Optional[str] = None, check_reserved: bool = True, additional_reserved_names: Optional[Sequence[str]] = None, + max_filename_len: Optional[int] = None, ) -> bool: """Check whether the ``filename`` is a valid name or not. @@ -355,11 +361,13 @@ def is_valid_filename( See Also: :py:func:`.validate_filename()` """ + if max_len is not None: + max_filename_len = max_len return FileNameValidator( platform=platform, min_len=min_len, - max_len=-1 if max_len is None else max_len, + max_filename_len=max_filename_len, fs_encoding=fs_encoding, check_reserved=check_reserved, additional_reserved_names=additional_reserved_names, @@ -370,13 +378,14 @@ def sanitize_filename( filename: PathType, replacement_text: str = "", platform: Optional[PlatformType] = None, - max_len: Optional[int] = _DEFAULT_MAX_FILENAME_LEN, + max_len: Optional[int] = None, fs_encoding: Optional[str] = None, check_reserved: Optional[bool] = None, null_value_handler: Optional[ValidationErrorHandler] = None, reserved_name_handler: Optional[ValidationErrorHandler] = None, additional_reserved_names: Optional[Sequence[str]] = None, validate_after_sanitize: bool = False, + max_filename_len: Optional[int] = None, ) -> PathType: """Make a valid filename from a string. @@ -401,9 +410,7 @@ def sanitize_filename( .. include:: platform.txt max_len: - Maximum byte length of the ``filename``. - Truncate the name length if the ``filename`` length exceeds this value. - Defaults to ``255``. + Alias for ``max_filename_len``. fs_encoding: Filesystem encoding that used to calculate the byte length of the filename. If |None|, get the value from the execution environment. @@ -433,6 +440,10 @@ def sanitize_filename( Case insensitive. validate_after_sanitize: Execute validation after sanitization to the file name. + max_filename_len: + Maximum byte length of the ``filename``. + Truncate the name length if the ``filename`` length exceeds this value. + Defaults to ``255``. Returns: Same type as the ``filename`` (str or PathLike object): @@ -445,6 +456,8 @@ def sanitize_filename( Example: :ref:`example-sanitize-filename` """ + if max_len is not None: + max_filename_len = max_len if check_reserved is not None: warnings.warn( @@ -457,7 +470,7 @@ def sanitize_filename( return FileNameSanitizer( platform=platform, - max_len=-1 if max_len is None else max_len, + max_filename_len=max_filename_len, fs_encoding=fs_encoding, null_value_handler=null_value_handler, reserved_name_handler=reserved_name_handler, diff --git a/pathvalidate/_filepath.py b/pathvalidate/_filepath.py index 38192a8..2cef0c6 100644 --- a/pathvalidate/_filepath.py +++ b/pathvalidate/_filepath.py @@ -26,7 +26,7 @@ class FilePathSanitizer(AbstractSanitizer): def __init__( self, - max_len: int = -1, + max_len: Optional[int] = None, fs_encoding: Optional[str] = None, platform: Optional[PlatformType] = None, null_value_handler: Optional[ValidationErrorHandler] = None, @@ -35,20 +35,27 @@ def __init__( normalize: bool = True, validate_after_sanitize: bool = False, validator: Optional[AbstractValidator] = None, + max_filename_len: Optional[int] = None, + max_filepath_len: Optional[int] = None, ) -> None: + if max_len is not None: + max_filepath_len = max_len + if validator: fpath_validator = validator else: fpath_validator = FilePathValidator( min_len=DEFAULT_MIN_LEN, - max_len=max_len, + max_filename_len=max_filename_len, + max_filepath_len=max_filepath_len, fs_encoding=fs_encoding, check_reserved=True, additional_reserved_names=additional_reserved_names, platform=platform, ) super().__init__( - max_len=max_len, + max_filename_len=max_filename_len, + max_filepath_len=max_filepath_len, fs_encoding=fs_encoding, validator=fpath_validator, null_value_handler=null_value_handler, @@ -60,7 +67,7 @@ def __init__( self._sanitize_regexp = self._get_sanitize_regexp() self.__fname_sanitizer = FileNameSanitizer( - max_len=self.max_len, + max_filename_len=self.max_filename_len, fs_encoding=fs_encoding, null_value_handler=null_value_handler, reserved_name_handler=reserved_name_handler, @@ -161,15 +168,20 @@ def reserved_keywords(self) -> Tuple[str, ...]: def __init__( self, min_len: int = DEFAULT_MIN_LEN, - max_len: int = -1, + max_len: Optional[int] = None, fs_encoding: Optional[str] = None, platform: Optional[PlatformType] = None, check_reserved: bool = True, additional_reserved_names: Optional[Sequence[str]] = None, + max_filename_len: Optional[int] = None, + max_filepath_len: Optional[int] = None, ) -> None: + if max_len is not None: + max_filepath_len = max_len super().__init__( min_len=min_len, - max_len=max_len, + max_filename_len=max_filename_len, + max_filepath_len=max_filepath_len, fs_encoding=fs_encoding, check_reserved=check_reserved, additional_reserved_names=additional_reserved_names, @@ -178,7 +190,8 @@ def __init__( self.__fname_validator = FileNameValidator( min_len=min_len, - max_len=max_len, + max_filename_len=self.max_filename_len, + fs_encoding=fs_encoding, check_reserved=check_reserved, additional_reserved_names=additional_reserved_names, platform=platform, @@ -206,10 +219,10 @@ def validate(self, value: PathType) -> None: ErrorAttrKey.BYTE_COUNT: byte_ct, } - if byte_ct > self.max_len: + if byte_ct > self.max_filepath_len: raise ValidationError( [ - f"file path is too long: expected<={self.max_len:d} bytes, actual={byte_ct:d} bytes" + f"file path is too long: expected<={self.max_filepath_len:d} bytes, actual={byte_ct:d} bytes" ], **err_kwargs, ) @@ -228,8 +241,7 @@ def validate(self, value: PathType) -> None: for entry in unicode_filepath.split("/"): if not entry or entry in (".", ".."): continue - - self.__fname_validator._validate_reserved_keywords(entry) + self.__fname_validator.validate(entry) if self._is_windows(include_universal=True): self.__validate_win_filepath(unicode_filepath) @@ -311,6 +323,8 @@ def validate_filepath( fs_encoding: Optional[str] = None, check_reserved: bool = True, additional_reserved_names: Optional[Sequence[str]] = None, + max_filename_len: Optional[int] = None, + max_filepath_len: Optional[int] = None, ) -> None: """Verifying whether the ``file_path`` is a valid file path or not. @@ -325,13 +339,7 @@ def validate_filepath( Minimum byte length of the ``file_path``. The value must be greater or equal to one. Defaults to ``1``. max_len (Optional[int], optional): - Maximum byte length of the ``file_path``. If the value is |None| or minus, - automatically determined by the ``platform``: - - - ``Linux``: 4096 - - ``macOS``: 1024 - - ``Windows``: 260 - - ``universal``: 260 + Alias for ``max_filepath_len``. fs_encoding (Optional[str], optional): Filesystem encoding that used to calculate the byte length of the file path. If |None|, get the value from the execution environment. @@ -340,6 +348,17 @@ def validate_filepath( Defaults to |True|. additional_reserved_names (Optional[Sequence[str]], optional): Additional reserved names to check. + max_filename_len: + Maximum byte length of each component of the ``file_path``. + Defaults to ``255``. + max_filepath_len: + Maximum byte length of the ``file_path``. If the value is |None| or minus, + automatically determined by the ``platform``: + + - ``Linux``: 4096 + - ``macOS``: 1024 + - ``Windows``: 260 + - ``universal``: 260 Raises: ValidationError (ErrorReason.INVALID_CHARACTER): @@ -359,11 +378,14 @@ def validate_filepath( `Naming Files, Paths, and Namespaces - Win32 apps | Microsoft Docs `__ """ + if max_len is not None: + max_filepath_len = max_len FilePathValidator( platform=platform, min_len=min_len, - max_len=-1 if max_len is None else max_len, + max_filename_len=max_filename_len, + max_filepath_len=max_filepath_len, fs_encoding=fs_encoding, check_reserved=check_reserved, additional_reserved_names=additional_reserved_names, @@ -378,6 +400,8 @@ def is_valid_filepath( fs_encoding: Optional[str] = None, check_reserved: bool = True, additional_reserved_names: Optional[Sequence[str]] = None, + max_filename_len: Optional[int] = None, + max_filepath_len: Optional[int] = None, ) -> bool: """Check whether the ``file_path`` is a valid name or not. @@ -393,11 +417,14 @@ def is_valid_filepath( See Also: :py:func:`.validate_filepath()` """ + if max_len is not None: + max_filepath_len = max_len return FilePathValidator( platform=platform, min_len=min_len, - max_len=-1 if max_len is None else max_len, + max_filename_len=max_filename_len, + max_filepath_len=max_filepath_len, fs_encoding=fs_encoding, check_reserved=check_reserved, additional_reserved_names=additional_reserved_names, @@ -416,6 +443,8 @@ def sanitize_filepath( additional_reserved_names: Optional[Sequence[str]] = None, normalize: bool = True, validate_after_sanitize: bool = False, + max_filename_len: Optional[int] = None, + max_filepath_len: Optional[int] = None, ) -> PathType: """Make a valid file path from a string. @@ -442,14 +471,7 @@ def sanitize_filepath( .. include:: platform.txt max_len: - Maximum byte length of the file path. - Truncate the path if the value length exceeds the `max_len`. - If the value is |None| or minus, ``max_len`` will automatically determined by the ``platform``: - - - ``Linux``: 4096 - - ``macOS``: 1024 - - ``Windows``: 260 - - ``universal``: 260 + Alias for ``max_filepath_len``. fs_encoding: Filesystem encoding that used to calculate the byte length of the file path. If |None|, get the value from the execution environment. @@ -481,6 +503,18 @@ def sanitize_filepath( If |True|, normalize the the file path. validate_after_sanitize: Execute validation after sanitization to the file path. + max_filename_len: + Maximum byte length of each component of the ``file_path``. + Truncate each component if the length exceeds this value. + Defaults to ``255``. + max_filepath_len: + Maximum byte length of the ``file_path``. If the value is |None| or minus, + automatically determined by the ``platform``: + + - ``Linux``: 4096 + - ``macOS``: 1024 + - ``Windows``: 260 + - ``universal``: 260 Returns: Same type as the argument (str or PathLike object): @@ -493,6 +527,8 @@ def sanitize_filepath( Example: :ref:`example-sanitize-file-path` """ + if max_len is not None: + max_filepath_len = max_len if check_reserved is not None: warnings.warn( @@ -505,7 +541,8 @@ def sanitize_filepath( return FilePathSanitizer( platform=platform, - max_len=-1 if max_len is None else max_len, + max_filename_len=max_filename_len, + max_filepath_len=max_filepath_len, fs_encoding=fs_encoding, normalize=normalize, null_value_handler=null_value_handler, diff --git a/test/test_filepath.py b/test/test_filepath.py index ece45cc..5dfd0e6 100644 --- a/test/test_filepath.py +++ b/test/test_filepath.py @@ -4,6 +4,7 @@ .. codeauthor:: Tsuyoshi Hombashi """ +import math import platform as m_platform import random import sys @@ -216,7 +217,53 @@ def test_normal_min_len(self, value, min_len, expected): def test_normal_max_len(self, value, platform, max_len, expected): kwargs = { "platform": platform, - "max_len": max_len, + "max_filepath_len": max_len, + "max_filename_len": math.inf, # ignore filename length checks + } + + if expected is None: + validate_filepath(value, **kwargs) + assert is_valid_filepath(value, **kwargs) + return + + with pytest.raises(ValidationError) as e: + validate_filepath(value, **kwargs) + assert e.value.reason == ErrorReason.INVALID_LENGTH + assert e.value.fs_encoding + assert e.value.byte_count + assert e.value.byte_count > 0 + + @pytest.mark.parametrize( + ["value", "platform", "max_path_len", "max_name_len", "expected"], + [ + ["a/" + "a" * 255, "linux", None, None, None], + ["a/" + "a" * 256, "linux", None, None, ErrorReason.INVALID_LENGTH], + ["a/" + "a" * 255, "windows", None, None, None], + ["a/" + "a" * 256, "windows", None, None, ErrorReason.INVALID_LENGTH], + ["a/" + "a" * 255, "universal", None, None, None], + ["a/" + "a" * 256, "universal", None, None, ErrorReason.INVALID_LENGTH], + ["/".join("a" * 255 for _ in range(16)), "linux", None, None, None], + [ + "/".join("a" * 255 for _ in range(17)), + "linux", + None, + None, + ErrorReason.INVALID_LENGTH, + ], + ["a/" + "a" * 255 + "/aa", "windows", None, None, None], + ["a/" + "a" * 255 + "/aa", "universal", None, None, None], + ["a/" + "a" * 255 + "/aaa", "windows", None, None, ErrorReason.INVALID_LENGTH], + ["a/" + "a" * 255 + "/aaa", "universal", None, None, ErrorReason.INVALID_LENGTH], + ["/".join("a" * 10 for _ in range(5)), "universal", 54, 10, None], + ["/".join("a" * 10 for _ in range(5)), "universal", 53, 10, ErrorReason.INVALID_LENGTH], + ["/".join("a" * 10 for _ in range(5)), "universal", 54, 9, ErrorReason.INVALID_LENGTH], + ], + ) + def test_max_name_max_path_len(self, value, platform, max_path_len, max_name_len, expected): + kwargs = { + "platform": platform, + "max_filepath_len": max_path_len, + "max_filename_len": max_name_len, } if expected is None: @@ -376,8 +423,13 @@ def test_relative_path(self, test_platform, value, expected): ], ) def test_normal_space_or_period_at_tail(self, platform, value): - validate_filepath(value, platform=platform) - assert is_valid_filepath(value, platform=platform) + if platform == "windows" or platform == "universal": + with pytest.raises(ValidationError): + validate_filepath(value, platform=platform) + assert not is_valid_filepath(value, platform=platform) + else: + validate_filepath(value, platform=platform) + assert is_valid_filepath(value, platform=platform) @pytest.mark.skipif(not is_faker_installed(), reason="requires faker") @pytest.mark.parametrize(