研究目的
在对输入路径进行核查时,需要检查其是否存在、是否可访问,并记录错误信息。
1.0
- 编写两个辅助函数
- check_directory_path(path: Path) -> dict:返回一个字典,包含是否存在、是否可读、错误信息等。
- check_file_path(path: Path) -> dict:同样返回一个字典,包含文件是否存在、是否可读、错误信息等。
from datetime import datetime
import logging
from pathlib import Path
from typing import Optional, Union, Tuple, Dict, Any
import os
import stat
import sys
def check_directory_path(path: Path) -> Tuple[bool, Dict[str, Any]]:
"""
检查目录路径的可访问性
参数:
path: 要检查的Path对象
返回:
(是否有效, 错误信息字典)
"""
result = {
"exists": False,
"readable": False,
"writable": False,
"error_messages": []
}
try:
# 检查路径是否存在
if not path.exists():
result["error_messages"].append("目录不存在")
return False, result
result["exists"] = True
# 检查是否是目录
if not path.is_dir():
result["error_messages"].append("路径不是目录")
return False, result
# 检查读权限
try:
# 尝试列出目录内容
next(path.iterdir())
result["readable"] = True
except PermissionError:
result["error_messages"].append("没有读取目录的权限")
except StopIteration:
# 空目录,但有读取权限
result["readable"] = True
except Exception as e:
result["error_messages"].append(f"读取目录时发生错误: {e}")
# 检查写权限
try:
# 尝试创建一个临时文件来测试写权限
test_file = path / ".write_test"
test_file.touch()
test_file.unlink() # 删除测试文件
result["writable"] = True
except PermissionError:
result["error_messages"].append("没有写入目录的权限")
except Exception as e:
result["error_messages"].append(f"测试目录写入权限时发生错误: {e}")
# 如果没有错误消息,则目录可访问
is_valid = len(result["error_messages"]) == 0
return is_valid, result
except Exception as e:
result["error_messages"].append(f"检查目录时发生意外错误: {e}")
return False, result
def check_file_path(path: Path) -> Tuple[bool, Dict[str, Any]]:
"""
检查文件路径的可访问性
参数:
path: 要检查的Path对象
返回:
(是否有效, 错误信息字典)
"""
result = {
"exists": False,
"readable": False,
"writable": False,
"error_messages": []
}
try:
# 检查路径是否存在
if not path.exists():
result["error_messages"].append("文件不存在")
return False, result
result["exists"] = True
# 检查是否是文件
if not path.is_file():
result["error_messages"].append("路径不是文件")
return False, result
# 检查读权限
try:
with open(path, 'rb'):
pass
result["readable"] = True
except PermissionError:
result["error_messages"].append("没有读取文件的权限")
except Exception as e:
result["error_messages"].append(f"读取文件时发生错误: {e}")
# 检查写权限
try:
# 尝试以追加模式打开文件来测试写权限
with open(path, 'ab'):
pass
result["writable"] = True
except PermissionError:
result["error_messages"].append("没有写入文件的权限")
except Exception as e:
result["error_messages"].append(f"测试文件写入权限时发生错误: {e}")
# 如果没有错误消息,则文件可访问
is_valid = len(result["error_messages"]) == 0
return is_valid, result
except Exception as e:
result["error_messages"].append(f"检查文件时发生意外错误: {e}")
return False, result
def check_path(path: Path) -> Tuple[bool, Dict[str, Any]]:
"""
检查路径的可访问性,根据路径类型调用相应的检查函数
参数:
path: 要检查的Path对象
返回:
(是否有效, 错误信息字典)
"""
logging.info(f"开始检查路径: {path}")
if path.is_file():
logging.info("路径是文件,进行文件检查")
return check_file_path(path)
elif path.is_dir():
logging.info("路径是目录,进行目录检查")
return check_directory_path(path)
else:
# 路径不存在或其他情况
result = {
"exists": False,
"readable": False,
"writable": False,
"error_messages": ["路径不存在或无法确定类型"]
}
logging.warning("路径不存在或无法确定类型")
return False, result
def get_user_input() -> Path:
"""
获取用户输入的路径并进行验证
返回:
标准化后的Path对象
"""
while True:
user_input = input("请输入路径: ").strip()
# 第一步:检查输入是否为空
if not user_input:
print("输入不能为空,请重新输入。")
logging.warning("用户输入了空路径")
continue
# 第二步:标准化路径
try:
normalized_path = normalize_path(user_input)
logging.info(f"标准化后的路径: {normalized_path}")
except Exception as e:
print(f"路径标准化失败: {e}")
logging.error(f"路径标准化失败: {user_input} - {e}")
continue
# 第三步:检查路径
is_valid, check_result = check_path(normalized_path)
# 记录检查结果
if is_valid:
logging.info("路径检查通过")
print("路径有效且可访问。")
return normalized_path
else:
logging.warning(f"路径检查未通过: {check_result['error_messages']}")
print("路径存在问题:")
for error in check_result['error_messages']:
print(f" - {error}")
# 询问用户是否继续使用此路径
choice = input("是否继续使用此路径? (y/n): ").strip().lower()
if choice == 'y':
logging.info("用户选择继续使用有问题的路径")
return normalized_path
else:
logging.info("用户选择重新输入路径")
print("请重新输入路径。")
1.5
使用 pathlib 模块优化后的代码,它更加符合 Python 3.0 的风格,并且利用了 pathlib 提供的面向对象方法来处理文件系统操作,更加简洁:
from datetime import datetime
import logging
from pathlib import Path
from typing import Optional, Union, List, Tuple
import sys
# 使用 pathlib 优化的检查函数
def check_directory_path(path: Path) -> Tuple[bool, List[str]]:
"""检查目录路径的可访问性"""
errors = []
if not path.exists():
errors.append("目录不存在")
return False, errors
if not path.is_dir():
errors.append("路径不是目录")
return False, errors
# 检查读权限 - 尝试列出目录内容
try:
# 使用 pathlib 的 iterdir() 方法检查读权限
next(path.iterdir())
except PermissionError:
errors.append("没有读取目录的权限")
except StopIteration:
# 空目录,但有读取权限
pass
except Exception as e:
errors.append(f"读取目录时发生错误: {e}")
# 检查写权限 - 尝试创建和删除临时文件
test_file = path / ".write_test.tmp"
try:
test_file.touch()
test_file.unlink() # 删除测试文件
except PermissionError:
errors.append("没有写入目录的权限")
except Exception as e:
errors.append(f"测试目录写入权限时发生错误: {e}")
return len(errors) == 0, errors
def check_file_path(path: Path) -> Tuple[bool, List[str]]:
"""检查文件路径的可访问性"""
errors = []
if not path.exists():
errors.append("文件不存在")
return False, errors
if not path.is_file():
errors.append("路径不是文件")
return False, errors
# 检查读权限 - 尝试打开文件
try:
with path.open('rb') as f:
# 尝试读取一个字节
f.read(1)
except PermissionError:
errors.append("没有读取文件的权限")
except Exception as e:
errors.append(f"读取文件时发生错误: {e}")
# 检查写权限 - 尝试以追加模式打开文件
try:
with path.open('ab'):
pass
except PermissionError:
errors.append("没有写入文件的权限")
except Exception as e:
errors.append(f"测试文件写入权限时发生错误: {e}")
return len(errors) == 0, errors
def check_path(path: Path) -> Tuple[bool, List[str]]:
"""检查路径可访问性,只在遇到问题时记录日志"""
if path.is_file():
return check_file_path(path)
elif path.is_dir():
return check_directory_path(path)
else:
# 路径不存在时记录日志
logging.warning(f"路径不存在或类型未知: {path}")
return False, ["路径不存在或无法确定类型"]
# 用户输入处理
def get_user_input() -> Path:
"""获取用户输入的路径并进行验证"""
while True:
user_input = input("请输入路径: ").strip()
if not user_input:
print("输入不能为空,请重新输入。")
logging.warning("用户输入了空路径")
continue
try:
normalized_path = normalize_path(user_input)
except Exception as e:
logging.error(f"路径标准化失败: {e}")
print(f"路径标准化失败: {e}")
continue
is_valid, errors = check_path(normalized_path)
if is_valid:
return normalized_path
print("路径存在问题:")
for error in errors:
print(f" - {error}")
choice = input("是否继续使用此路径? (y/n): ").strip().lower()
if choice == 'y':
logging.warning(f"用户选择继续使用有问题的路径: {normalized_path}")
return normalized_path
else:
print("请重新输入路径。")
主要优化点:
-
完全使用 pathlib 方法:
- 使用
path.exists(),path.is_dir(),path.is_file()等 pathlib 方法替代 os 模块的函数 - 使用
path.iterdir()检查目录读取权限 - 使用
path.touch()和path.unlink()测试目录写入权限 - 使用
path.open()方法检查文件读写权限
- 使用
-
遵循 EAFP (Easier to Ask for Forgiveness than Permission) 原则:
- 直接尝试执行操作并捕获异常,而不是先检查权限
- 这提供了更准确的权限检查,因为权限可能在检查和实际操作之间发生变化
-
Pythonic 的代码风格:
- 充分利用 pathlib 的面向对象特性
- 代码更简洁、更易读
2.0
方法1:使用 os.access()
# 检查写权限
if not os.access(path, os.W_OK):
errors.append("没有写入目录的权限")
-
优点:
- 非常快速,不涉及任何磁盘 I/O
- 不会创建任何临时文件
-
缺点:
- 可能存在 TOCTOU (Time of Check to Time of Use) 竞争条件
- 在某些特殊情况下可能不准确(如磁盘已满、权限在检查后立即更改等)
方法2:使用 pathlib
# 如果目录不存在,尝试创建它来测试写权限
if not path.exists():
try:
path.mkdir(parents=True, exist_ok=True)
# 创建成功,说明有写权限
path.rmdir() # 删除测试目录
except PermissionError:
errors.append("没有在当前目录创建子目录的权限")
except Exception as e:
errors.append(f"测试目录创建权限时发生错误: {e}")
结合两种方法
from datetime import datetime
import logging
from pathlib import Path
from typing import Optional, Union, List, Tuple, Dict
import os
import sys
import tempfile
def check_directory_path(path: Path) -> Tuple[bool, List[str]]:
"""检查目录路径的可访问性"""
errors = []
if not path.exists():
errors.append("目录不存在")
return False, errors
if not path.is_dir():
errors.append("路径不是目录")
return False, errors
# 检查读权限
if not os.access(path, os.R_OK):
errors.append("没有读取目录的权限")
else:
# 对通过快速检查的进行实际验证
try:
# 尝试列出目录内容(但限制数量)
with os.scandir(path) as it:
next(it) # 尝试获取第一个条目
except StopIteration:
pass # 空目录是正常的
except PermissionError:
errors.append("实际读取目录失败:权限不足")
except Exception as e:
errors.append(f"读取目录时发生错误: {e}")
# 检查写权限 - 使用快速检查加验证的方式
if not os.access(path, os.W_OK):
errors.append("没有写入目录的权限")
else:
# 对通过快速检查的进行实际验证
# 替换原来的写权限检查
try:
with tempfile.TemporaryDirectory(dir=path) as _:
pass # 成功创建即表示有写权限
except PermissionError:
errors.append("实际写入目录失败:权限不足")
except Exception as e:
errors.append(f"测试目录写入权限时发生错误: {e}")
return len(errors) == 0, errors
def check_file_path(path: Path) -> Tuple[bool, List[str]]:
"""检查文件路径的可访问性"""
errors = []
if not path.exists():
errors.append("文件不存在")
return False, errors
if not path.is_file():
errors.append("路径不是文件")
return False, errors
# 检查读权限
# 使用os.access进行快速初步检查
if not os.access(path, os.R_OK):
errors.append("没有读取文件的权限")
else:
# 对通过初步检查的进行实际读取验证
try:
with path.open('rb') as f:
f.read(1) # 尝试读取一个字节
except PermissionError:
errors.append("没有读取文件的权限")
except Exception as e:
errors.append(f"读取文件时发生错误: {e}")
# 检查写权限
if not os.access(path, os.W_OK):
errors.append("没有写入文件的权限")
else:
# 对通过初步检查的进行实际写入验证
try:
with path.open('ab'):
pass # 尝试以追加模式打开
except PermissionError:
errors.append("没有写入文件的权限")
except Exception as e:
errors.append(f"测试文件写入权限时发生错误: {e}")
return len(errors) == 0, errors
def check_path(path: Path) -> Tuple[bool, List[str]]:
"""检查路径可访问性"""
if path.is_file():
return check_file_path(path)
elif path.is_dir():
return check_directory_path(path)
else:
logging.warning(f"路径不存在或类型未知: {path}")
return False, ["路径不存在或无法确定类型"]
# 用户输入处理
def get_user_input() -> Path:
"""获取用户输入的路径并进行验证"""
while True:
user_input = input("请输入路径: ").strip()
if not user_input:
print("输入不能为空,请重新输入。")
logging.warning("用户输入了空路径")
continue
try:
normalized_path = normalize_path(user_input)
except Exception as e:
logging.error(f"路径标准化失败: {e}")
print(f"路径标准化失败: {e}")
continue
is_valid, errors = check_path(normalized_path)
if is_valid:
return normalized_path
print("路径存在问题:")
for error in errors:
print(f" - {error}")
choice = input("是否继续使用此路径? (y/n): ").strip().lower()
if choice == 'y':
logging.warning(f"用户选择继续使用有问题的路径: {normalized_path}")
return normalized_path
else:
print("请重新输入路径。")
1369

被折叠的 条评论
为什么被折叠?



