From 898b52edcb47bcb3e9d6106e74ca73e74ea01e70 Mon Sep 17 00:00:00 2001 From: sillylaird Date: Thu, 3 Sep 2026 00:33:59 +0000 Subject: import live www.sillylaird.ca webroot --- .../bug-detective/examples/debugging-workflow.py | 241 +++++++++++++ .../bug-detective/examples/debugging-workflow.sh | 400 +++++++++++++++++++++ .../examples/error-handling-patterns.py | 305 ++++++++++++++++ 3 files changed, 946 insertions(+) create mode 100644 .agents/skills/bug-detective/examples/debugging-workflow.py create mode 100755 .agents/skills/bug-detective/examples/debugging-workflow.sh create mode 100644 .agents/skills/bug-detective/examples/error-handling-patterns.py (limited to '.agents/skills/bug-detective/examples') diff --git a/.agents/skills/bug-detective/examples/debugging-workflow.py b/.agents/skills/bug-detective/examples/debugging-workflow.py new file mode 100644 index 0000000..0f76d27 --- /dev/null +++ b/.agents/skills/bug-detective/examples/debugging-workflow.py @@ -0,0 +1,241 @@ +""" +调试流程示例 + +这个示例展示了完整的调试流程,从发现问题到解决问题。 +""" + +import logging + +# 配置日志 +logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + + +# ============================================ +# 问题 1:IndexError +# ============================================ + +def get_item(items, index): + """ + 问题:直接访问索引可能导致 IndexError + + 错误现象: + IndexError: list index out of range + """ + # ❌ 有问题的代码 + # return items[index] + + # ✅ 修复后的代码 + if 0 <= index < len(items): + return items[index] + else: + logger.warning(f"索引 {index} 超出范围 [0, {len(items)})") + return None + + +# ============================================ +# 问题 2:TypeError - 字符串拼接 +# ============================================ + +def format_message(name, count): + """ + 问题:尝试拼接字符串和数字 + + 错误现象: + TypeError: can only concatenate str (not "int") to str + """ + # ❌ 有问题的代码 + # return name + ": " + count + + # ✅ 修复后的代码 + return f"{name}: {count}" + # 或 + # return name + ": " + str(count) + + +# ============================================ +# 问题 3:KeyError +# ============================================ + +def get_user_info(users, user_id): + """ + 问题:直接访问字典中可能不存在的键 + + 错误现象: + KeyError: 'user_123' + """ + # ❌ 有问题的代码 + # return users[user_id] + + # ✅ 修复后的代码 - 方法1:使用 get() + return users.get(user_id, None) + + # ✅ 修复后的代码 - 方法2:检查键是否存在 + # if user_id in users: + # return users[user_id] + # return None + + +# ============================================ +# 问题 4:AttributeError - None 对象 +# ============================================ + +def process_data(data_provider): + """ + 问题:data_provider 可能返回 None + + 错误现象: + AttributeError: 'NoneType' object has no attribute 'process' + """ + data = data_provider.get_data() + + # ❌ 有问题的代码 + # return data.process() + + # ✅ 修复后的代码 + if data is not None: + return data.process() + else: + logger.error("数据为 None,无法处理") + return None + + +# ============================================ +# 问题 5:修改正在迭代的列表 +# ============================================ + +def remove_even_numbers(numbers): + """ + 问题:迭代时修改列表导致跳过元素 + + 错误现象: + 某些偶数没有被移除 + """ + # ❌ 有问题的代码 + # for num in numbers: + # if num % 2 == 0: + # numbers.remove(num) + # return numbers + + # ✅ 修复后的代码 - 方法1:列表推导式 + return [num for num in numbers if num % 2 != 0] + + # ✅ 修复后的代码 - 方法2:使用副本 + # for num in numbers[:]: + # if num % 2 == 0: + # numbers.remove(num) + # return numbers + + +# ============================================ +# 调试技巧示例 +# ============================================ + +def debug_with_logging(data): + """ + 使用日志追踪问题 + """ + logger.debug(f"输入数据: {data}") + + # 步骤 1 + processed = step1(data) + logger.debug(f"步骤1结果: {processed}") + + # 步骤 2 + result = step2(processed) + logger.debug(f"步骤2结果: {result}") + + return result + + +def step1(data): + """模拟步骤1""" + return [x * 2 for x in data] + + +def step2(data): + """模拟步骤2""" + return sum(data) + + +# ============================================ +# 异常处理示例 +# ============================================ + +def safe_divide(a, b): + """ + 正确的异常处理模式 + """ + try: + result = a / b + logger.info(f"{a} / {b} = {result}") + return result + except ZeroDivisionError: + logger.error(f"除数不能为零: {b}") + return None + except TypeError as e: + logger.error(f"类型错误: {e}") + return None + + +# ============================================ +# 使用断言进行调试 +# ============================================ + +def calculate_discount(price, discount_rate): + """ + 使用断言验证假设 + """ + # 断言:价格应该是正数 + assert price > 0, f"价格应该是正数,实际: {price}" + + # 断言:折扣率应该在 0-1 之间 + assert 0 <= discount_rate <= 1, f"折扣率应该在 0-1 之间,实际: {discount_rate}" + + discounted_price = price * (1 - discount_rate) + + # 断言:折扣后的价格应该小于原价 + assert discounted_price <= price, "折扣价格应该小于原价" + + return discounted_price + + +# ============================================ +# 测试代码 +# ============================================ + +if __name__ == "__main__": + print("=" * 50) + print("调试示例") + print("=" * 50) + + # 测试 get_item + print("\n1. 测试 get_item:") + items = ['a', 'b', 'c'] + print(f"get_item(items, 1) = {get_item(items, 1)}") + print(f"get_item(items, 10) = {get_item(items, 10)}") + + # 测试 format_message + print("\n2. 测试 format_message:") + print(f"format_message('Count', 42) = {format_message('Count', 42)}") + + # 测试 get_user_info + print("\n3. 测试 get_user_info:") + users = {'user_1': 'Alice', 'user_2': 'Bob'} + print(f"get_user_info(users, 'user_1') = {get_user_info(users, 'user_1')}") + print(f"get_user_info(users, 'user_999') = {get_user_info(users, 'user_999')}") + + # 测试 remove_even_numbers + print("\n4. 测试 remove_even_numbers:") + numbers = [1, 2, 3, 4, 5, 6] + print(f"原始: {numbers}") + print(f"结果: {remove_even_numbers(numbers)}") + + # 测试 safe_divide + print("\n5. 测试 safe_divide:") + print(f"safe_divide(10, 2) = {safe_divide(10, 2)}") + print(f"safe_divide(10, 0) = {safe_divide(10, 0)}") + + # 测试 calculate_discount + print("\n6. 测试 calculate_discount:") + print(f"calculate_discount(100, 0.2) = {calculate_discount(100, 0.2)}") diff --git a/.agents/skills/bug-detective/examples/debugging-workflow.sh b/.agents/skills/bug-detective/examples/debugging-workflow.sh new file mode 100755 index 0000000..1ff74a5 --- /dev/null +++ b/.agents/skills/bug-detective/examples/debugging-workflow.sh @@ -0,0 +1,400 @@ +#!/bin/bash +# +# Shell 脚本调试流程示例 +# 展示常见的 Bash 脚本错误和调试方法 +# + +# ============================================ +# 调试设置 +# ============================================ + +# 取消注释以启用调试模式 +# set -x # 打印每个命令 +# set -e # 错误时退出 +# set -u # 未定义变量报错 +# set -o pipefail # 管道中任何命令失败则失败 + +# 或组合使用 +# set -xeuo pipefail # 严格模式 + + +# ============================================ +# 错误处理函数 +# ============================================ + +# 错误退出函数 +die() { + local message="$1" + local exit_code="${2:-1}" + echo "Error: $message" >&2 + exit "$exit_code" +} + +# 记录函数 +log() { + local level="$1" + shift + echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" >&2 +} + +log_info() { log "INFO" "$@"; } +log_warn() { log "WARN" "$@"; } +log_error() { log "ERROR" "$@"; } + + +# ============================================ +# 问题 1:未引用的变量 +# ============================================ + +# 错误示例:变量未引用 +demo_unquoted_variable() { + echo "=== 问题 1:未引用的变量 ===" + + local name="John Doe" + + # 错误:变量未引用,空值会导致语法错误 + # if [ $name = "John Doe" ]; then + # echo "Match" + # fi + + # 正确:始终引用变量 + if [ "$name" = "John Doe" ]; then + log_info "变量匹配: $name" + fi +} + + +# ============================================ +# 问题 2:命令失败继续执行 +# ============================================ + +demo_command_failure() { + echo "=== 问题 2:命令失败继续执行 ===" + + # 错误:cd 失败后继续执行 + # cd /nonexistent_directory + # rm -rf file.txt # 会删除当前目录的文件! + + # 正确:检查命令是否成功 + cd /tmp || die "无法切换到 /tmp 目录" + log_info "成功切换到目录: $(pwd)" + + cd - > /dev/null || true +} + + +# ============================================ +# 问题 3:循环中的变量作用域(管道问题) +# ============================================ + +demo_pipeline_scope() { + echo "=== 问题 3:管道中的变量作用域 ===" + + local count=0 + + # 错误:管道创建子 shell,外部变量不改变 + # echo -e "1\n2\n3" | while read line; do + # count=$((count + 1)) + # done + # echo "Count: $count" # 输出 0 + + # 正确:使用重定向 + while read line; do + count=$((count + 1)) + done < <(echo -e "1\n2\n3") + log_info "计数结果: $count" +} + + +# ============================================ +# 问题 4:数组操作 +# ============================================ + +demo_array_operations() { + echo "=== 问题 4:数组操作 ===" + + local fruits=("apple" "banana" "cherry") + + # 错误的数组访问 + # echo $fruits[1] # 输出 apple[1] + + # 正确的数组访问 + log_info "第一个元素: ${fruits[0]}" + log_info "第二个元素: ${fruits[1]}" + log_info "所有元素: ${fruits[@]}" + log_info "数组长度: ${#fruits[@]}" + + # 遍历数组 + for fruit in "${fruits[@]}"; do + log_info "水果: $fruit" + done +} + + +# ============================================ +# 问题 5:字符串比较 +# ============================================ + +demo_string_comparison() { + echo "=== 问题 5:字符串比较 ===" + + local name="John" + + # 错误:数字比较使用 = + # if [ $age = 18 ]; then + + # 正确:字符串比较 + if [[ "$name" == "John" ]]; then + log_info "字符串匹配" + fi + + # 正确:数字比较 + local age=18 + if [ "$age" -eq 18 ]; then + log_info "数字匹配" + fi +} + + +# ============================================ +# 问题 6:算术运算 +# ============================================ + +demo_arithmetic() { + echo "=== 问题 6:算术运算 ===" + + local a=10 + local b=5 + + # 错误:使用 let 或 $(()) + # result = a + b # 这是命令调用 + + # 正确的算术运算 + local result=$((a + b)) + log_info "加法: $a + $b = $result" + + result=$((a - b)) + log_info "减法: $a - $b = $result" + + result=$((a * b)) + log_info "乘法: $a * $b = $result" + + result=$((a / b)) + log_info "除法: $a / $b = $result" + + # 使用 let + let result=a+b + log_info "let 加法: $result" +} + + +# ============================================ +# 参数验证 +# ============================================ + +validate_arguments() { + echo "=== 参数验证 ===" + + # 检查参数数量 + if [ $# -lt 2 ]; then + die "用法: $0 <文件> <目录>" 2 + fi + + local file="$1" + local dir="$2" + + # 检查文件存在 + if [ ! -f "$file" ]; then + die "文件不存在: $file" 3 + fi + + # 检查目录存在 + if [ ! -d "$dir" ]; then + die "目录不存在: $dir" 4 + fi + + log_info "参数验证通过" + log_info "文件: $file" + log_info "目录: $dir" +} + + +# ============================================ +# 使用 trap 进行清理 +# ============================================ + +demo_trap() { + echo "=== 使用 trap 进行清理 ===" + + # 设置清理函数 + cleanup() { + log_info "执行清理操作..." + # 这里可以执行清理操作 + } + + # 捕获退出信号 + trap cleanup EXIT + + # 捕获错误信号 + trap 'log_error "发生错误,行号: $LINENO"' ERR + + # 捕获中断信号 + trap 'log_warn "脚本被中断"; cleanup; exit 130' INT + + log_info "执行一些操作..." + # 模拟操作 + sleep 1 +} + + +# ============================================ +# 调试技巧示例 +# ============================================ + +demo_debugging() { + echo "=== 调试技巧 ===" + + # 1. 使用 echo 调试 + local value="test" + echo "[DEBUG] value = $value" >&2 + + # 2. 使用 printf 格式化输出 + printf "[DEBUG] Count: %d, Name: %s\n" 42 "John" >&2 + + # 3. 检查变量是否设置 + if [ -z "${unset_var+x}" ]; then + log_warn "变量 unset_var 未设置" + fi + + # 4. 显示调用栈 + log_info "调用栈:" + local i=0 + while caller $i; do + ((i++)) + done 2>/dev/null || true +} + + +# ============================================ +# 文件操作错误处理 +# ============================================ + +demo_file_operations() { + echo "=== 文件操作错误处理 ===" + + local tmpfile=$(mktemp) || die "无法创建临时文件" + + # 确保文件被删除 + trap "rm -f '$tmpfile'" EXIT + + # 写入文件 + echo "Test content" > "$tmpfile" || die "无法写入文件: $tmpfile" + + # 读取文件 + local content + content=$(cat "$tmpfile") || die "无法读取文件: $tmpfile" + + log_info "文件内容: $content" + + # trap 会自动清理 +} + + +# ============================================ +# 带重试的操作 +# ============================================ + +demo_retry() { + echo "=== 带重试的操作 ===" + + local max_attempts=3 + local attempt=1 + + while [ $attempt -le $max_attempts ]; do + log_info "尝试 $attempt/$max_attempts..." + + # 模拟可能失败的操作 + if [ $attempt -eq 2 ]; then + log_info "成功!" + return 0 + fi + + log_warn "失败,重试..." + ((attempt++)) + sleep 1 + done + + log_error "所有尝试都失败了" + return 1 +} + + +# ============================================ +# 检查依赖 +# ============================================ + +check_dependencies() { + echo "=== 检查依赖 ===" + + local required_commands=("curl" "jq" "git") + + for cmd in "${required_commands[@]}"; do + if ! command -v "$cmd" &> /dev/null; then + log_error "缺少依赖: $cmd" + return 1 + fi + log_info "✓ $cmd 可用" + done + + log_info "所有依赖都已满足" +} + + +# ============================================ +# 主函数 +# ============================================ + +main() { + echo "============================================" + echo "Shell 脚本调试示例" + echo "============================================" + + # 运行各个示例 + demo_unquoted_variable + echo "" + + demo_command_failure + echo "" + + demo_pipeline_scope + echo "" + + demo_array_operations + echo "" + + demo_string_comparison + echo "" + + demo_arithmetic + echo "" + + demo_trap + echo "" + + demo_debugging + echo "" + + demo_file_operations + echo "" + + demo_retry + echo "" + + check_dependencies + echo "" + + log_info "所有示例执行完成" +} + +# 运行主函数 +main "$@" diff --git a/.agents/skills/bug-detective/examples/error-handling-patterns.py b/.agents/skills/bug-detective/examples/error-handling-patterns.py new file mode 100644 index 0000000..57d197e --- /dev/null +++ b/.agents/skills/bug-detective/examples/error-handling-patterns.py @@ -0,0 +1,305 @@ +""" +错误处理模式示例 + +展示各种错误处理的最佳实践 +""" + +import logging +from typing import Optional, List, Dict, Any +from functools import wraps + +logger = logging.getLogger(__name__) + + +# ============================================ +# 模式 1:具体异常捕获 +# ============================================ + +def read_file(filepath: str) -> Optional[str]: + """ + 捕获具体异常而不是宽泛的 Exception + """ + try: + with open(filepath, 'r') as f: + return f.read() + except FileNotFoundError: + logger.error(f"文件不存在: {filepath}") + return None + except PermissionError: + logger.error(f"没有权限读取文件: {filepath}") + return None + except UnicodeDecodeError: + logger.error(f"文件编码错误: {filepath}") + return None + + +# ============================================ +# 模式 2:带重试的操作 +# ============================================ + +def retry_operation(max_attempts: int = 3): + """ + 装饰器:自动重试失败的操作 + """ + def decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): + for attempt in range(1, max_attempts + 1): + try: + return func(*args, **kwargs) + except Exception as e: + if attempt == max_attempts: + logger.error(f"操作失败,已重试 {max_attempts} 次: {e}") + raise + logger.warning(f"操作失败,第 {attempt} 次重试...") + return None + return wrapper + return decorator + + +@retry_operation(max_attempts=3) +def unstable_api_call() -> Dict[str, Any]: + """ + 模拟不稳定的 API 调用 + """ + import random + if random.random() < 0.7: # 70% 失败率 + raise ConnectionError("API 连接失败") + return {"status": "success", "data": "result"} + + +# ============================================ +# 模式 3:上下文管理器处理资源 +# ============================================ + +class DatabaseConnection: + """ + 自定义上下文管理器,确保资源正确释放 + """ + def __init__(self, connection_string: str): + self.connection_string = connection_string + self.connection = None + + def __enter__(self): + logger.info(f"连接数据库: {self.connection_string}") + self.connection = f"Connection to {self.connection_string}" + return self.connection + + def __exit__(self, exc_type, exc_val, exc_tb): + if exc_type is not None: + logger.error(f"发生异常: {exc_val}") + logger.info("关闭数据库连接") + # 清理资源 + self.connection = None + return False # 不抑制异常 + + +# ============================================ +# 模式 4:链式异常(保留原始异常) +# ============================================ + +def validate_and_process(data: Dict[str, Any]) -> Any: + """ + 使用 raise from 保留原始异常链 + """ + try: + # 验证数据 + if 'value' not in data: + raise ValueError("数据中缺少 'value' 字段") + + value = data['value'] + if not isinstance(value, (int, float)): + raise TypeError(f"value 应该是数字,实际类型: {type(value)}") + + # 处理数据 + return value * 2 + + except (ValueError, TypeError) as e: + # 保留原始异常并添加上下文 + raise RuntimeError(f"数据处理失败: {data}") from e + + +# ============================================ +# 模式 5:结果对象模式(不使用异常) +# ============================================ + +class Result: + """ + 结果对象模式:封装成功/失败状态 + """ + def __init__(self, success: bool, value: Any = None, error: str = None): + self.success = success + self.value = value + self.error = error + + @classmethod + def ok(cls, value: Any) -> 'Result': + return cls(success=True, value=value) + + @classmethod + def err(cls, error: str) -> 'Result': + return cls(success=False, error=error) + + def is_ok(self) -> bool: + return self.success + + def is_err(self) -> bool: + return not self.success + + def unwrap(self) -> Any: + if not self.success: + raise ValueError(f"尝试解包错误结果: {self.error}") + return self.value + + def unwrap_or(self, default: Any) -> Any: + return self.value if self.success else default + + +def safe_divide_result(a: float, b: float) -> Result: + """ + 使用结果对象而不是异常 + """ + if b == 0: + return Result.err(f"除数不能为零: {b}") + + try: + return Result.ok(a / b) + except Exception as e: + return Result.err(f"计算失败: {e}") + + +# ============================================ +# 模式 6:多项验证错误收集 +# ============================================ + +class ValidationError(Exception): + """自定义验证错误""" + def __init__(self, errors: List[str]): + self.errors = errors + super().__init__("\n".join(errors)) + + +def validate_user(data: Dict[str, Any]) -> None: + """ + 收集所有验证错误而不是遇到第一个就停止 + """ + errors = [] + + if 'name' not in data: + errors.append("缺少 'name' 字段") + elif not isinstance(data['name'], str): + errors.append("'name' 应该是字符串") + elif len(data['name']) < 2: + errors.append("'name' 长度应该至少为 2") + + if 'age' not in data: + errors.append("缺少 'age' 字段") + elif not isinstance(data['age'], int): + errors.append("'age' 应该是整数") + elif data['age'] < 0 or data['age'] > 150: + errors.append("'age' 应该在 0-150 之间") + + if 'email' in data and '@' not in data['email']: + errors.append("'email' 格式不正确") + + if errors: + raise ValidationError(errors) + + +# ============================================ +# 模式 7:默认值和回退 +# ============================================ + +def get_config(config: Dict[str, Any], key: str, default: Any = None) -> Any: + """ + 安全获取配置,支持多级键和默认值 + """ + if '.' in key: + # 支持嵌套键,如 "database.host" + keys = key.split('.') + value = config + for k in keys: + if isinstance(value, dict) and k in value: + value = value[k] + else: + return default + return value + + # 简单键 + return config.get(key, default) + + +# ============================================ +# 模式 8:优雅降级 +# ============================================ + +def get_user_preferences(user_id: int) -> Dict[str, Any]: + """ + 尝试多种方法获取用户偏好,优雅降级 + """ + # 尝试 1:从缓存获取 + try: + return _get_from_cache(user_id) + except Exception as e: + logger.warning(f"从缓存获取失败: {e}") + + # 尝试 2:从数据库获取 + try: + return _get_from_database(user_id) + except Exception as e: + logger.warning(f"从数据库获取失败: {e}") + + # 尝试 3:使用默认配置 + logger.info("使用默认配置") + return _get_default_preferences() + + +def _get_from_cache(user_id: int) -> Dict[str, Any]: + # 模拟缓存失败 + raise ConnectionError("缓存连接失败") + + +def _get_from_database(user_id: int) -> Dict[str, Any]: + # 模拟数据库失败 + raise ConnectionError("数据库连接失败") + + +def _get_default_preferences() -> Dict[str, Any]: + return { + "theme": "light", + "language": "zh-CN", + "notifications": True + } + + +# ============================================ +# 测试代码 +# ============================================ + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + + print("=" * 50) + print("错误处理模式示例") + print("=" * 50) + + # 测试结果对象模式 + print("\n1. 结果对象模式:") + result1 = safe_divide_result(10, 2) + print(f"10 / 2 = {result1.unwrap()}") + + result2 = safe_divide_result(10, 0) + print(f"10 / 0 = {result2.unwrap_or('N/A')} ({result2.error})") + + # 测试优雅降级 + print("\n2. 优雅降级:") + prefs = get_user_preferences(123) + print(f"用户偏好: {prefs}") + + # 测试上下文管理器 + print("\n3. 上下文管理器:") + try: + with DatabaseConnection("localhost:5432") as conn: + print(f"连接: {conn}") + except Exception as e: + print(f"操作失败: {e}") -- cgit v1.2.3