-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalidation.py
More file actions
155 lines (118 loc) · 4.98 KB
/
validation.py
File metadata and controls
155 lines (118 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
from dataclasses import dataclass
from collections.abc import Callable, Iterable
import os
# =========================
# Validation Result
# =========================
@dataclass
class ValidationResult:
is_valid: bool
message: str = ""
# =========================
# Types
# =========================
Validator = Callable[[str], ValidationResult]
# =========================
# Validation Engine
# =========================
def validate(
value: str,
validators: Iterable[Validator],
fail_fast: bool = True
) -> list[ValidationResult]:
"""
Runs a sequence of validation rules over a given input value.
This function applies each validator to the input string and collects
validation results. It supports two execution modes:
1. Fail-fast mode:
Stops at the first validation failure and returns immediately.
2. Aggregate mode:
Runs all validators and returns all validation failures.
Args:
value (str):
The input value to be validated.
validators (Iterable[Validator]):
A collection of validator functions. Each validator must accept
a string and return a ValidationResult.
fail_fast (bool, optional):
If True, validation stops at the first failure.
If False, all validators are executed and all failures are collected.
Default is True.
Returns:
list[ValidationResult]:
A list of validation results that represent failures.
If the list is empty, the value is considered valid.
"""
results = []
for validator in validators:
result = validator(value)
if not result.is_valid:
results.append(result)
if fail_fast:
return results
return results
# =========================
# Input validators
# =========================
def is_not_blank(value: str) -> ValidationResult:
if not value:
return ValidationResult(False, "Input must not be blank!")
return ValidationResult(True)
def is_in_range(min_val: int, max_val: int, custom_error: str = None) -> Validator:
"""Factory: Returns a validator that evaluates if a numeric string falls within a specific range.
Args:
min_val (int): The inclusive lower bound of the range.
max_val (int): The inclusive upper bound of the range.
custom_error (str, optional): A custom error message if validation fails.
Returns:
Validator: A callable function dedicated solely to range boundary checking.
"""
def validator(value: str) -> ValidationResult:
numeric_value = int(value)
if min_val <= numeric_value <= max_val:
return ValidationResult(True)
error_msg = custom_error or f"Value out of range. Expected between {min_val} and {max_val}."
return ValidationResult(False, error_msg)
return validator
# =========================
# Domain validators
# =========================
def is_valid_srcdir(value: str) -> ValidationResult:
"""Validator to ensure the input path points to an actual existing directory."""
if not os.path.exists(value):
return ValidationResult(False, f"The path '{value}' does not exist.")
if not os.path.isdir(value):
return ValidationResult(False, f"The path '{value}' is not a directory.")
return ValidationResult(True)
def is_different_from_source(srcdir: str) -> Validator:
"""Factory: Returns a validator that ensures the destination path differs from the source path.
This validator prevents self-referential file operations by ensuring that
the provided target directory is not physically identical to the source directory.
Args:
srcdir (str): The absolute or relative path of the source directory.
Returns:
Validator: A callable that accepts a destination path string and returns
a ValidationResult indicating whether the paths are distinctly separated.
"""
def validator(value: str) -> ValidationResult:
abs_dest = os.path.abspath(value)
if abs_dest == os.path.abspath(srcdir):
return ValidationResult(
is_valid=False,
message=f"The destination path cannot be identical to the source path: '{abs_dest}'"
)
return ValidationResult(is_valid=True)
return validator
def is_not_existing_file(value: str) -> ValidationResult:
"""Validator to ensure the input path is not an already existing regular file.
This prevents the application from attempting to create a directory
or move files into a path that is already occupied by a standard file.
"""
if not value:
return ValidationResult(True)
if os.path.exists(value) and os.path.isfile(value):
return ValidationResult(
is_valid=False,
message=f"The path '{value}' already exists as a file. Please specify a directory."
)
return ValidationResult(is_valid=True)