0.1.8
Loading...
Searching...
No Matches
files.py
Go to the documentation of this file.
1"""
2File and path validation utilities for PyHelios.
3
4Provides secure file path validation with consistent error handling,
5extending the patterns from Context._validate_file_path.
6"""
7
8import os
9from pathlib import Path
10from typing import List, Optional
11
12from .exceptions import ValidationError, create_validation_error
13
14
15def validate_file_path(filename: str, expected_extensions: List[str] = None,
16 must_exist: bool = True, param_name: str = "filename",
17 function_name: str = None) -> str:
18 """
19 Validate and normalize file path with security checks.
20
21 Based on Context._validate_file_path but enhanced with ValidationError
22 and standardized error messaging.
23
24 Args:
25 filename: File path to validate
26 expected_extensions: List of allowed file extensions (e.g., ['.ply', '.obj'])
27 must_exist: Whether file must already exist
28 param_name: Parameter name for error messages
29 function_name: Function name for error messages
30
31 Returns:
32 Normalized absolute path
33
34 Raises:
35 ValidationError: If path is invalid, doesn't exist, or has wrong extension
36 """
37 if not isinstance(filename, (str, Path)):
38 raise create_validation_error(
39 f"Parameter must be a string or Path",
40 param_name=param_name,
41 function_name=function_name,
42 expected_type="string or Path",
43 actual_value=filename,
44 suggestion="Provide file path as a string or pathlib.Path object."
45 )
46
47 if not filename:
48 raise create_validation_error(
49 f"Parameter cannot be empty",
50 param_name=param_name,
51 function_name=function_name,
52 expected_type="non-empty string",
53 actual_value=filename,
54 suggestion="Provide a valid file path."
55 )
56
57 # Convert to Path for consistent handling
58 try:
59 path = Path(filename)
60 normalized = path.resolve()
61 except (OSError, ValueError) as e:
62 raise create_validation_error(
63 f"Parameter contains invalid characters or path: {filename} ({e})",
64 param_name=param_name,
65 function_name=function_name,
66 expected_type="valid file path",
67 actual_value=filename,
68 suggestion="Ensure the path contains valid characters and no illegal sequences."
69 )
70
71 # Security check - prevent path traversal (following Context pattern)
72 abs_path = os.path.abspath(str(path))
73 normalized_path = os.path.normpath(abs_path)
74 if abs_path != normalized_path:
75 raise create_validation_error(
76 f"Invalid file path (potential path traversal): {filename}",
77 param_name=param_name,
78 function_name=function_name,
79 expected_type="safe file path",
80 actual_value=filename,
81 suggestion="Avoid path traversal sequences like '../' in file paths."
82 )
83
84 # Extension validation
85 if expected_extensions:
86 if path.suffix.lower() not in [ext.lower() for ext in expected_extensions]:
87 extensions_str = ", ".join(expected_extensions)
88 raise create_validation_error(
89 f"Parameter must have one of these extensions: {extensions_str}, got '{path.suffix}'",
90 param_name=param_name,
91 function_name=function_name,
92 expected_type=f"file with extension {extensions_str}",
93 actual_value=filename,
94 suggestion=f"Use a file with one of these extensions: {extensions_str}"
95 )
96
97 # Existence check
98 if must_exist and not normalized.exists():
99 raise create_validation_error(
100 f"File does not exist: {normalized}",
101 param_name=param_name,
102 function_name=function_name,
103 expected_type="existing file",
104 actual_value=filename,
105 suggestion=f"Ensure the file exists at: {normalized}"
106 )
107
108 return str(normalized)
109
110
111def validate_directory_path(directory: str, must_exist: bool = True,
112 create_if_missing: bool = False,
113 param_name: str = "directory",
114 function_name: str = None) -> str:
115 """
116 Validate and normalize directory path.
117
118 Args:
119 directory: Directory path to validate
120 must_exist: Whether directory must already exist
121 create_if_missing: Create directory if it doesn't exist
122 param_name: Parameter name for error messages
123 function_name: Function name for error messages
124
125 Returns:
126 Normalized absolute path
128 Raises:
129 ValidationError: If path is invalid or doesn't exist
130 """
131 if not isinstance(directory, (str, Path)):
132 raise create_validation_error(
133 f"Parameter must be a string or Path",
134 param_name=param_name,
135 function_name=function_name,
136 expected_type="string or Path",
137 actual_value=directory,
138 suggestion="Provide directory path as a string or pathlib.Path object."
139 )
140
141 if not directory:
142 raise create_validation_error(
143 f"Parameter cannot be empty",
144 param_name=param_name,
145 function_name=function_name,
146 expected_type="non-empty string",
147 actual_value=directory,
148 suggestion="Provide a valid directory path."
149 )
150
151 try:
152 path = Path(directory)
153 normalized = path.resolve()
154 except (OSError, ValueError) as e:
155 raise create_validation_error(
156 f"Parameter contains invalid characters or path: {directory} ({e})",
157 param_name=param_name,
158 function_name=function_name,
159 expected_type="valid directory path",
160 actual_value=directory,
161 suggestion="Ensure the path contains valid characters and no illegal sequences."
162 )
163
164 if must_exist and not normalized.exists():
165 if create_if_missing:
166 try:
167 normalized.mkdir(parents=True, exist_ok=True)
168 except OSError as e:
169 raise create_validation_error(
170 f"Cannot create directory: {normalized} ({e})",
171 param_name=param_name,
172 function_name=function_name,
173 expected_type="creatable directory path",
174 actual_value=directory,
175 suggestion=f"Ensure you have write permissions to create: {normalized}"
176 )
177 else:
178 raise create_validation_error(
179 f"Directory does not exist: {normalized}",
180 param_name=param_name,
181 function_name=function_name,
182 expected_type="existing directory",
183 actual_value=directory,
184 suggestion=f"Ensure the directory exists at: {normalized}"
185 )
186
187 if normalized.exists() and not normalized.is_dir():
188 raise create_validation_error(
189 f"Path exists but is not a directory: {normalized}",
190 param_name=param_name,
191 function_name=function_name,
192 expected_type="directory path",
193 actual_value=directory,
194 suggestion=f"The path {normalized} exists but is a file, not a directory."
195 )
196
197 return str(normalized)
str validate_file_path(str filename, List[str] expected_extensions=None, bool must_exist=True, str param_name="filename", str function_name=None)
Validate and normalize file path with security checks.
Definition files.py:36
str validate_directory_path(str directory, bool must_exist=True, bool create_if_missing=False, str param_name="directory", str function_name=None)
Validate and normalize directory path.
Definition files.py:130