347 lines
8.6 KiB
Python
347 lines
8.6 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
工具模块
|
|
提供各种工具函数和验证器类
|
|
"""
|
|
|
|
import socket
|
|
import re
|
|
from abc import ABC, abstractmethod
|
|
from typing import Optional, List
|
|
|
|
|
|
class Validator(ABC):
|
|
"""验证器抽象基类"""
|
|
|
|
@abstractmethod
|
|
def validate(self, value: str) -> bool:
|
|
"""
|
|
验证值
|
|
|
|
Args:
|
|
value: 要验证的值
|
|
|
|
Returns:
|
|
是否有效
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_error_message(self, value: str) -> str:
|
|
"""
|
|
获取错误消息
|
|
|
|
Args:
|
|
value: 无效的值
|
|
|
|
Returns:
|
|
错误消息
|
|
"""
|
|
pass
|
|
|
|
|
|
class IPAddressValidator(Validator):
|
|
"""IP地址验证器"""
|
|
|
|
IPV4_PATTERN = r'^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
|
|
IPV6_PATTERN = r'^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^::$|^:(?::)+(?:[0-9a-fA-F]{1,4})?$'
|
|
|
|
def validate(self, value: str) -> bool:
|
|
"""验证IP地址"""
|
|
return bool(
|
|
re.match(self.IPV4_PATTERN, value) or
|
|
re.match(self.IPV6_PATTERN, value)
|
|
)
|
|
|
|
def get_error_message(self, value: str) -> str:
|
|
"""获取错误消息"""
|
|
return f"无效的IP地址: {value}"
|
|
|
|
|
|
class HostnameValidator(Validator):
|
|
"""主机名验证器"""
|
|
|
|
def validate(self, value: str) -> bool:
|
|
"""验证主机名"""
|
|
if len(value) > 253:
|
|
return False
|
|
|
|
hostname = value.rstrip('.')
|
|
pattern = r"^(?!-)[A-Z0-9-]{1,63}(?<!-)$"
|
|
|
|
return all(
|
|
re.match(pattern, label, re.IGNORECASE)
|
|
for label in hostname.split('.')
|
|
)
|
|
|
|
def get_error_message(self, value: str) -> str:
|
|
"""获取错误消息"""
|
|
return f"无效的主机名: {value}"
|
|
|
|
|
|
class RangeValidator(Validator):
|
|
"""范围验证器"""
|
|
|
|
def __init__(self, min_val: float, max_val: float):
|
|
"""
|
|
初始化范围验证器
|
|
|
|
Args:
|
|
min_val: 最小值
|
|
max_val: 最大值
|
|
"""
|
|
self.min_val = min_val
|
|
self.max_val = max_val
|
|
|
|
def validate(self, value: str) -> bool:
|
|
"""验证数值是否在范围内"""
|
|
try:
|
|
num = float(value)
|
|
return self.min_val <= num <= self.max_val
|
|
except ValueError:
|
|
return False
|
|
|
|
def get_error_message(self, value: str) -> str:
|
|
"""获取错误消息"""
|
|
return f"值 {value} 不在范围 [{self.min_val}, {self.max_val}] 内"
|
|
|
|
|
|
class NetworkUtils:
|
|
"""网络工具类(静态方法集合)"""
|
|
|
|
@staticmethod
|
|
def resolve_hostname(hostname: str) -> Optional[str]:
|
|
"""
|
|
解析主机名为IP地址
|
|
|
|
Args:
|
|
hostname: 主机名
|
|
|
|
Returns:
|
|
IP地址,解析失败返回None
|
|
"""
|
|
try:
|
|
return socket.gethostbyname(hostname)
|
|
except socket.gaierror:
|
|
return None
|
|
|
|
@staticmethod
|
|
def is_port_open(host: str, port: int, timeout: float = 2.0) -> bool:
|
|
"""
|
|
检查端口是否开放
|
|
|
|
Args:
|
|
host: 主机地址
|
|
port: 端口号
|
|
timeout: 超时时间(秒)
|
|
|
|
Returns:
|
|
端口是否开放
|
|
"""
|
|
try:
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sock.settimeout(timeout)
|
|
result = sock.connect_ex((host, port))
|
|
sock.close()
|
|
return result == 0
|
|
except Exception:
|
|
return False
|
|
|
|
@staticmethod
|
|
def validate_ip(ip: str) -> bool:
|
|
"""
|
|
验证IP地址
|
|
|
|
Args:
|
|
ip: IP地址
|
|
|
|
Returns:
|
|
是否有效
|
|
"""
|
|
validator = IPAddressValidator()
|
|
return validator.validate(ip)
|
|
|
|
@staticmethod
|
|
def validate_hostname(hostname: str) -> bool:
|
|
"""
|
|
验证主机名
|
|
|
|
Args:
|
|
hostname: 主机名
|
|
|
|
Returns:
|
|
是否有效
|
|
"""
|
|
validator = HostnameValidator()
|
|
return validator.validate(hostname)
|
|
|
|
|
|
class Formatter:
|
|
"""格式化工具类"""
|
|
|
|
@staticmethod
|
|
def format_bytes(bytes_size: int) -> str:
|
|
"""
|
|
格式化字节大小
|
|
|
|
Args:
|
|
bytes_size: 字节数
|
|
|
|
Returns:
|
|
格式化后的字符串
|
|
"""
|
|
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
|
|
if bytes_size < 1024.0:
|
|
return f"{bytes_size:.2f} {unit}"
|
|
bytes_size /= 1024.0
|
|
return f"{bytes_size:.2f} PB"
|
|
|
|
@staticmethod
|
|
def format_duration(seconds: float) -> str:
|
|
"""
|
|
格式化时长
|
|
|
|
Args:
|
|
seconds: 秒数
|
|
|
|
Returns:
|
|
格式化后的字符串
|
|
"""
|
|
if seconds < 60:
|
|
return f"{seconds:.1f}秒"
|
|
elif seconds < 3600:
|
|
return f"{seconds / 60:.1f}分钟"
|
|
elif seconds < 86400:
|
|
return f"{seconds / 3600:.1f}小时"
|
|
else:
|
|
return f"{seconds / 86400:.1f}天"
|
|
|
|
@staticmethod
|
|
def format_percentage(value: float, total: float) -> str:
|
|
"""
|
|
格式化百分比
|
|
|
|
Args:
|
|
value: 数值
|
|
total: 总数
|
|
|
|
Returns:
|
|
格式化后的百分比字符串
|
|
"""
|
|
if total == 0:
|
|
return "0.00%"
|
|
return f"{(value / total * 100):.2f}%"
|
|
|
|
|
|
class MathUtils:
|
|
"""数学工具类"""
|
|
|
|
@staticmethod
|
|
def safe_divide(numerator: float, denominator: float, default: float = 0.0) -> float:
|
|
"""
|
|
安全除法
|
|
|
|
Args:
|
|
numerator: 分子
|
|
denominator: 分母
|
|
default: 除零时的默认值
|
|
|
|
Returns:
|
|
除法结果或默认值
|
|
"""
|
|
try:
|
|
return numerator / denominator if denominator != 0 else default
|
|
except Exception:
|
|
return default
|
|
|
|
@staticmethod
|
|
def clamp(value: float, min_val: float, max_val: float) -> float:
|
|
"""
|
|
限制数值范围
|
|
|
|
Args:
|
|
value: 数值
|
|
min_val: 最小值
|
|
max_val: 最大值
|
|
|
|
Returns:
|
|
限制后的数值
|
|
"""
|
|
return max(min_val, min(value, max_val))
|
|
|
|
@staticmethod
|
|
def average(values: List[float]) -> float:
|
|
"""
|
|
计算平均值
|
|
|
|
Args:
|
|
values: 数值列表
|
|
|
|
Returns:
|
|
平均值
|
|
"""
|
|
if not values:
|
|
return 0.0
|
|
return sum(values) / len(values)
|
|
|
|
|
|
class ValidatorFactory:
|
|
"""验证器工厂类"""
|
|
|
|
@staticmethod
|
|
def create_ip_validator() -> IPAddressValidator:
|
|
"""创建IP地址验证器"""
|
|
return IPAddressValidator()
|
|
|
|
@staticmethod
|
|
def create_hostname_validator() -> HostnameValidator:
|
|
"""创建主机名验证器"""
|
|
return HostnameValidator()
|
|
|
|
@staticmethod
|
|
def create_range_validator(min_val: float, max_val: float) -> RangeValidator:
|
|
"""创建范围验证器"""
|
|
return RangeValidator(min_val, max_val)
|
|
|
|
|
|
# 保留向后兼容的函数接口
|
|
def validate_ip_address(ip: str) -> bool:
|
|
"""验证IP地址(向后兼容)"""
|
|
return NetworkUtils.validate_ip(ip)
|
|
|
|
|
|
def validate_hostname(hostname: str) -> bool:
|
|
"""验证主机名(向后兼容)"""
|
|
return NetworkUtils.validate_hostname(hostname)
|
|
|
|
|
|
def resolve_hostname(hostname: str) -> Optional[str]:
|
|
"""解析主机名(向后兼容)"""
|
|
return NetworkUtils.resolve_hostname(hostname)
|
|
|
|
|
|
def is_port_open(host: str, port: int, timeout: float = 2.0) -> bool:
|
|
"""检查端口(向后兼容)"""
|
|
return NetworkUtils.is_port_open(host, port, timeout)
|
|
|
|
|
|
def format_bytes(bytes_size: int) -> str:
|
|
"""格式化字节(向后兼容)"""
|
|
return Formatter.format_bytes(bytes_size)
|
|
|
|
|
|
def format_duration(seconds: float) -> str:
|
|
"""格式化时长(向后兼容)"""
|
|
return Formatter.format_duration(seconds)
|
|
|
|
|
|
def safe_divide(numerator: float, denominator: float, default: float = 0.0) -> float:
|
|
"""安全除法(向后兼容)"""
|
|
return MathUtils.safe_divide(numerator, denominator, default)
|
|
|
|
|
|
def clamp(value: float, min_val: float, max_val: float) -> float:
|
|
"""限制范围(向后兼容)"""
|
|
return MathUtils.clamp(value, min_val, max_val)
|