mcpField
P
python-developer

python-developer

github.com/gvre开发739 次安装19 周前更新
代码仓库

关于 python-developer

用于创建和维护 Python 3.12+ 项目的开发 Skill,规定项目结构、类型标注、测试、代码检查、错误处理和安全实践,并涵盖不同版本的语言特性。

主要功能

  1. 01Python 3.12+ 开发指南
  2. 02src/ 项目布局与 pyproject.toml
  3. 03类型标注和现代语言特性
  4. 04Ruff、ty 或 mypy 与 pytest 实践
  5. 05安全和错误处理指南

适用场景

  1. 01启动新的 Python 项目
  2. 02按项目规范审查代码
  3. 03编写带类型标注的异步代码
  4. 04配置测试与质量检查

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/gvre/skills --skill python-developer

安装后在 Claude Code 中输入/python-developer确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
core-language-features.md
logging-guide.md
python-3.13-features.md
python-3.14-features.md
REFERENCE.md
security-guide.md
testing-guide.md

详细介绍

Python 开发者

编写生产级 Python 3.12+ 项目的指南,遵循当前最佳实践。

版本支持

本指南涵盖:

功能版本矩阵
功能3.123.133.14
类型参数语法(def func[T]())✅✅✅
类型语句(type X = ...)✅✅✅
模式匹配(match/case)✅✅✅
异常组(except*)✅✅✅
TaskGroup✅✅✅
用于类型收窄的 TypeIs❌✅✅
ReadOnly 类型提示❌✅✅
改进的 REPL❌✅✅
无括号的 except(PEP 758)❌❌✅
实验性 JIT❌❌✅
无 GIL 的自由线程模式❌❌✅

快速参考

核心要求(3.12+):

  • 最低要求为 Python 3.12+
  • 使用 pyproject.toml 管理所有配置
  • 在所有地方使用类型提示
  • 使用 Ruff 进行格式化和代码检查
  • 使用 ty 或 mypy 进行类型检查
  • 使用 pytest 进行测试
  • 采用 src/ 目录布局

类型检查器对比:

功能mypyty
状态✅ 稳定、成熟(1.0+)⚠️ Beta(0.0.22,2026 年 3 月)
速度标准快 10–100 倍
语言PythonRust
生态系统丰富快速发展
IDE 支持通用VS Code、PyCharm、Neovim(内置语言服务器)
可用于生产环境✅ 是⚠️ 正在快速成熟,适用于新项目
最适合生产系统、现有代码库新项目、快速迭代
维护者Python 社区Astral(Ruff/uv 的创建者)
配置[tool.mypy][tool.ty]
建议现有代码库、风险规避型团队新项目的默认选择

项目覆盖项

工具设置的解析方式取决于任务:

  • 审查现有代码 / PR: 从代码仓库读取 pyproject.toml。 项目的实际配置是 python-version、line-length、ruff 规则、类型检查器、测试 框架和项目布局的唯一依据。不要覆盖项目 已定义的配置。
  • 创建新项目: 如果尚无 pyproject.toml, 请使用以下默认值。

本地覆盖项(见“可覆盖项”)在两种模式下均适用,并且 优先级高于项目配置和以下默认值。

新项目的默认值
设置默认值
python-version3.14
line-length120
type-checkerty
test-frameworkpytest
test-directorytests/
project-layoutsrc/
docstring-styleGoogle
ruff-selectE, F, W, I, B, C4, UP
可覆盖项

覆盖项不限于上述工具默认值。代码仓库可以提供任何 影响此技能运行方式的额外上下文:

  • 领域 / 业务背景: 服务的功能、集成的外部系统,以及 AI 仅阅读代码 无法获知的关键业务不变量。
  • 审查行为: 要评论的优先级、要跳过的内容、置信度阈值、评论数量上限。
  • 代码库状态: 正在进行的迁移、有意保留的已知技术债务、正在逐步淘汰的旧模式(以免 AI 将其标记出来)。
  • 团队约定: 此代码库特有且不同于通用最佳实践的模式。
  • 外部注意事项: 下游使用者、部署限制、第三方依赖中的已知限制。
如何覆盖

将覆盖规则放入你的 Agent 平台使用的本地规则文件中:

  • Cursor: .cursor/rules/python-overrides.mdc(包含 globs: "**/*.py")
  • Claude Code: 仓库根目录下的 CLAUDE.md
  • 通用 / agentskills.io: 仓库根目录下的 AGENTS.md

只写入与默认规则不同的内容,或 AI 无法从代码中推断出的上下文。

覆盖示例(适用于上述任一文件):

此仓库的 Python 技能覆盖规则:

领域背景: 此服务是支付网关适配器,集成了 Stripe 和 Adyen API。错误处理和幂等性至关重要——始终指出支付流程中缺少重试逻辑或存在非幂等变更的问题。

评审行为:

  • 仅指出优先级 1(严重)和优先级 2(重要)的问题
  • 不要评论文档字符串风格或命名偏好
  • 每个 PR 最多发表 5 条评审意见

代码库状态:

  • 正在从 Flask 迁移到 FastAPI;混合使用不同模式是有意为之
  • legacy/ 目录计划在 Q3 移除;不要评审其中的文件

工具覆盖项(仅当 pyproject.toml 未定义时适用):

  • docstring-style:NumPy

项目结构

project-name/
├── src/
│   └── package_name/
│       ├── __init__.py
│       └── core.py
├── tests/
│   └── test_core.py
├── pyproject.toml
└── README.md

规则:

  • 始终使用 src/ 布局(绝不将代码放在项目根目录)
  • 包中始终包含 init.py
  • 模块名使用下划线:my_module.py,而非 my-module.py

配置(pyproject.toml)

基础配置
[project]
name = "your-project"
version = "0.1.0"
description = "Your description"
requires-python = ">=3.14"
dependencies = [
    "httpx>=0.28.1",
]

[project.optional-dependencies]
dev = [
    "ruff>=0.14.10",
    "ty>=0.0.5",
]
test = [
    "pytest>=9.0.2",
    "pytest-asyncio>=1.3.0",
    "pytest-cov>=7.0.0",
]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.ruff]
line-length = 120

[tool.ruff.lint]
select = ["E", "F", "W", "I", "B", "C4", "UP"]

[tool.ty]
python-version = "3.14"

类型提示(Python 3.12+)

必须在所有地方使用:

  • 所有函数参数和返回值类型
  • 类属性
  • 无法一目了然的模块级变量

现代语法(3.10+):

# Use | for unions (not Optional/Union)
def process(data: str | None) -> dict[str, int | float]:
    ...

# Use lowercase generics (not List/Dict)
def filter_items(items: list[str]) -> list[str]:
    ...

# Use collections.abc for abstract types
from collections.abc import Sequence, Mapping, Iterable

def process_items(items: Sequence[str]) -> Iterable[str]:
    """Process items - accepts list, tuple, or any sequence."""
    return (item.upper() for item in items)

Python 3.12+ 类型特性(PEP 695):

# Type parameters with new syntax
def first[T](items: list[T]) -> T | None:
    return items[0] if items else None

# Type statement for aliases
type Point = tuple[float, float]

# Generic classes
class Container[T]:
    def __init__(self, value: T) -> None:
        self.value = value

    def get(self) -> T:
        return self.value

高级模式: 有关全面的类型提示模式、Protocol、TypedDict、Literal 和重载示例,请参阅 references/core-language-features.md。

禁止事项:

  • 绝不从 typing 导入 List、Dict、Tuple、Optional、Union(它们已由内置泛型和 | 语法取代)。仍可导入 Any、Self、Protocol、TypeVar、TypeGuard、TypeIs。
  • 函数绝不能缺少类型标注

现代特性

数据类(Python 3.7+,从 3.10+ 起支持 slots)
from dataclasses import dataclass
from typing import Self

@dataclass(slots=True)
class User:
    id: int
    name: str
    email: str | None = None

    def with_email(self, email: str) -> Self:
        return User(id=self.id, name=self.name, email=email)
模式匹配——基础(Python 3.10+)
def handle_command(cmd: str) -> str:
    match cmd:
        case "quit" | "exit":
            return "Exiting"
        case "help":
            return "Showing help"
        case _:
            return f"Unknown: {cmd}"
Functools 模式
from functools import cache, lru_cache

@cache  # Unbounded cache for pure functions
def expensive_computation(n: int) -> int:
    return n ** 2

@lru_cache(maxsize=128)  # Limited cache
def fetch_data(url: str) -> dict[str, Any]:
    ...
Pathlib(而非 os.path)
from pathlib import Path

config_dir = Path.home() / ".config" / "myapp"
config_file = config_dir / "config.json"
上下文管理器
import os
from collections.abc import Iterator
from contextlib import contextmanager

@contextmanager
def temporary_setting(name: str, value: str) -> Iterator[None]:
    old_value = os.getenv(name)
    os.environ[name] = value
    try:
        yield
    finally:
        if old_value is None:
            del os.environ[name]
        else:
            os.environ[name] = old_value

异步编程

基础异步编程(所有版本)
import asyncio
import httpx

async def fetch_url(url: str, timeout: float = 10.0) -> str:
    async with httpx.AsyncClient(timeout=timeout) as client:
        response = await client.get(url)
        return response.text
任务组(Python 3.11+)
async def fetch_all(urls: list[str], timeout: float = 30.0) -> list[str]:
    async with asyncio.timeout(timeout):
        async with asyncio.TaskGroup() as tg:
            tasks = [tg.create_task(fetch_url(url)) for url in urls]
    return [task.result() for task in tasks]

高级模式: 有关装饰器、描述符、海象运算符、上下文变量、高级模式匹配和元类,请参阅 references/core-language-features.md。

测试(pytest)

关键模式:

  • 使用 pytest(绝不使用 unittest)
  • 使用参数化测试覆盖多种场景
  • 模拟外部依赖
  • 使用固件进行设置和清理
  • 使用 @pytest.mark.asyncio 标记异步测试

有关全面的测试模式、异步测试、模拟、固件和覆盖率配置,请参阅 references/testing-guide.md。

代码质量

Ruff 配置
[tool.ruff]
line-length = 120

[tool.ruff.lint]
select = ["E", "F", "W", "I", "B", "C4", "UP"]
类型检查
# ty (default for new projects)
[tool.ty]
python-version = "3.14"

# mypy (for existing projects that already use it)
[tool.mypy]
python_version = "3.14"
strict = true
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
命名约定
  • 函数/变量: snake_case
  • 类: PascalCase
  • 常量: UPPER_SNAKE_CASE
  • 私有项: 以 _ 为前缀

✅ 正确:calculate_average()、UserManager、MAX_RETRIES ❌ 错误:CalculateAverage()、calc_avg()、userManager

错误处理

标准异常处理(所有版本):

try:
    risky_operation()
except ValueError as e:
    logger.error(f"Invalid value: {e}")
except TimeoutError:
    logger.warning("Operation timed out")

捕获 Exception——可接受的情况:

# ✅ Top-level handler (API endpoint, CLI) - handle gracefully
@app.route("/api/data")
def get_data():
    try:
        return process_data()
    except Exception as e:
        logger.exception("Request failed")
        return {"error": "Internal server error"}, 500

# ✅ Batch processing - log and continue
for item in items:
    try:
        process(item)
    except Exception as e:
        logger.exception(f"Failed to process {item}")
        continue

# ✅ Add context and wrap
except Exception as e:
    raise ProcessingError(f"Failed for {item_id}") from e

# ❌ Never silently swallow
except Exception:
    pass

异常组(Python 3.11+):

try:
    async with asyncio.TaskGroup() as tg:
        tg.create_task(task1())
        tg.create_task(task2())
except* ValueError as eg:
    for exc in eg.exceptions:
        handle_value_error(exc)

日志记录

基础设置:

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s | %(levelname)-8s | %(name)s - %(message)s",
)

生产环境最佳实践: 有关结构化日志记录(structlog 与 python-json-logger)、异步日志记录、关联 ID、请求跟踪和性能优化,请参阅 references/logging-guide.md。

依赖项

[project]
dependencies = [
    "httpx>=0.28.1",
    "pydantic>=2.5.0",
]

[project.optional-dependencies]
dev = [
    "ruff>=0.14.10",
    "ty>=0.0.5",
]
test = [
    "pytest>=9.0.2",
    "pytest-asyncio>=1.3.0",
    "pytest-cov>=7.0.0",
]

反模式(绝对不要这样做)

  • ❌ 可变默认参数:def func(items=[])
  • ❌ 裸 except:(会捕获 SystemExit、KeyboardInterrupt)
  • ❌ 默默吞掉异常:except Exception: pass
  • ❌ 对用户输入使用 eval/exec
  • ❌ 使用 os.path(请改用 pathlib)
  • ❌ from typing import List, Dict(请改用内置泛型)
  • ❌ 将密钥或 API 密钥硬编码
  • ❌ 使用未参数化的 SQL 查询

文档

def calculate_average(
    numbers: list[float],
    *,
    weights: list[float] | None = None
) -> float:
    """Calculate the average of numbers.

    Args:
        numbers: List of numbers to average
        weights: Optional weights for weighted average

    Returns:
        The calculated average

    Raises:
        ValueError: If numbers is empty

    Examples:
        >>> calculate_average([1, 2, 3])
        2.0
    """

**规则:**采用 Google 或 NumPy 文档字符串风格。为公共 API 编写文档字符串。类型应写在签名中,而非文档字符串中。

Python 3.13 和 3.14 功能

安全最佳实践

关键规则:

  • ✅ 始终验证并清理用户输入
  • ✅ 始终对 SQL 使用参数化查询
  • ✅ 始终对密码进行哈希处理(Argon2 或 bcrypt)
  • ✅ 始终从环境中加载密钥
  • ✅ 始终对外部 API 使用 HTTPS
  • ❌ 绝不硬编码密钥
  • ❌ 绝不使用字符串拼接来编写 SQL
  • ❌ 绝不记录敏感数据

有关输入验证、SQL 注入防护、路径遍历防护、密码哈希、密钥管理和全面安全模式,请参见 references/security-guide.md。

检查清单

面向代码作者

编写 Python 代码时:

  • 使用 Python 3.12+ 功能
  • 所有函数均包含类型提示
  • 使用现代类型语法(|、小写泛型、collections.abc)
  • 数据类使用 slots=True
  • 使用 Pathlib,而非 os.path
  • 使用 F 字符串进行格式化
  • 捕获特定异常
  • 异步代码具备适当的错误处理和超时设置
  • 编写测试(pytest),并使用模拟
  • 遵循安全最佳实践
  • 为公共 API 编写文档字符串
  • 不硬编码密钥
  • Ruff 和类型检查器(ty/mypy)检查通过
  • 使用 src/ 布局
面向代码审查者

审查质量规则(发表评论前请遵守):

  • 只有在确信时才发表评论。 如果不确定某处是否确实有问题,就不要发表评论。误报比遗漏一条建议更浪费审查者和作者的时间。
  • 重信号,轻噪声。 有 2 条关键发现的审查,比提出 15 条置信度参差不齐的建议更有价值。评论应少而精。
  • 默认范围:仅限 Priority 1 和 2。 除非仓库明确要求覆盖 Priority 3 问题,否则不要对此类问题发表评论。
  • 绝不针对纯粹的风格偏好发表评论,尤其是配置的代码检查工具未指出的问题。若 ruff 检查通过,则风格可以接受。
  • 尊重代码库的当前状态。 如果代码正处于已知迁移过程中、使用正在逐步淘汰的旧模式,或存在已记录的技术债,除非覆盖规则另有要求,否则不要指出这些模式。
  • 有领域上下文时请加以利用。 支付服务缺少超时设置是关键问题;一次性迁移脚本缺少同样的超时设置则未必如此。应结合代码的实际用途权衡发现,而不只是套用通用规则。
  • 遵守审查行为覆盖规则。 如果仓库设置了评论数量上限、置信度阈值或排除某些类别,请严格遵守这些约束。
  • 发现是传感器数据,而非最终裁决。 绝不要因为审查结果干净就暗示改动可以安全合并;应说明你检查了什么,以及哪些内容无法检查。借来的信心本身就是风险。你可能是这个 PR 的多位审查者之一;你的审查结果干净,只能说明符合本技能关注的优先级,并不能代表整个 PR 没有问题。
  • 坚守职责范围。 专注于此处定义的优先事项(Python 正确性、安全、资源安全)。不要扩展到其他审查者更适合覆盖的通用检查;独立、专业的信号比重复重叠更有价值。
审查优先级

审查 Python 代码时,先根据影响范围对每个变更文件进行分类:

  • 关键(身份验证、支付、加密、数据删除、数据库迁移,以及任何处理不可信输入或个人身份信息的内容):严格审查——将临界情况的发现上调一个优先级。
  • 标准(业务逻辑、API、服务层):按常规优先级规则处理。
  • 低(配置、文档、生成的代码、仅测试变更):仅报告 P1;不要吹毛求疵。

然后确定发现的优先级:

优先级 1 - 关键问题(必须修复):

  1. 安全漏洞(SQL 注入、硬编码密钥、路径遍历、不安全的 eval、提示词注入——不可信/用户控制的文本在没有防护的情况下流入 LLM 调用;此风险潜藏于运行时数据中,无法从差异中看出)
  2. 正确性问题(可变默认值、异步错误、缺少超时、裸 except)
  3. 资源泄漏(数据库连接、文件句柄、网络套接字未使用上下文管理器)
  4. 新近被抑制的安全检查器检查项——本次差异中新增的 # noqa S...;将被抑制的警告视为当前问题。预先存在的抑制项不在审查范围内。
  5. 可疑的测试变更——断言被弱化或改写以匹配新的(可能有问题的)行为、测试被删除或跳过、变更代码路径的覆盖被移除。当差异同时修改代码及其测试时,确认测试仍然断言的是正确行为,而不只是当前行为。

优先级 2 - 重要改进(应该修复):

  1. 错误处理(异常范围过宽、缺少针对性、错误被吞掉)
  2. 类型安全(缺少类型提示、使用禁止的 typing 导入,例如 List/Optional)
  3. 测试缺口(缺少测试、覆盖率低于 80%、未模拟外部依赖)

优先级 3 - 可改进项(可考虑修复):

  1. 采用现代 Python 写法(用 os.path 而非 pathlib、使用旧式格式化)
  2. 文档(缺少或不完整的文档字符串)
  3. 代码质量(命名规范、可以使用模式匹配)
  4. 性能(缺少 @cache、可以使用 TaskGroups)。当影响明确时,上调为优先级 2:大规模输入下的 O(n^2)、热点路径中反复进行昂贵调用,或异步代码中存在阻塞式同步 I/O。
  5. 重复造轮子——新代码重新实现了现有标准库或内部辅助函数,而没有复用它们。如果重新实现的辅助函数处理安全敏感逻辑,则上调为优先级 2。
  6. 通用安全关卡的放宽——在非关键测试上使用 @pytest.mark.skip、使用没有理由说明的无关 # noqa 或 # type: ignore、放宽覆盖率阈值、放宽 ruff 或类型检查器配置。

PR 审查工作流

提示: 对于涉及多种语言的 PR,请使用 pr-reviewer 技能——它可以在一次审查中协调 Go、Python 等语言的专项审查。

收到 PR 审查请求(通过编号、URL 或分支名称)时:

步骤 1:收集上下文
  • 使用 gh pr view 和 gh pr diff 获取 PR 描述、元数据和差异
  • 快速筛查: 过于庞大的差异、大量重写测试或含糊/缺失意图说明本身就是问题。应标记“PR 过大,无法有把握地审查——建议拆分”,而不是草率通过。
  • 阅读 pyproject.toml,了解项目实际的工具配置
  • 阅读任何本地覆盖规则(.cursor/rules/、AGENTS.md、CLAUDE.md)
  • 获取现有审查评论(gh pr view --comments),跳过其他审查者或作者已经提出的问题——不要重复报告
  • 根据 PR 描述,确定变更的文件及其用途
步骤 2:审查
  • 应用上述审查质量规则和优先级
  • 根据 PR 声明的意图评估变更——指出偏离 PR 所述目标的地方,而不只是泛泛的代码质量问题
  • 忽略本地覆盖规则排除的文件(例如冻结目录)
第 3 步:呈现发现
  • 按优先级分组向用户展示发现,并提供文件路径和 行号
  • 在用户明确批准之前,切勿发布到 GitHub
  • 如果没有值得标记的问题,请说明这一点——审查结果无问题也是 有效结论
第 4 步:发布(仅在获得批准后)
  • 将每项发现作为单独的行内审查评论,发布到 PR 中的具体 行
  • 使用单次审查提交(而不是逐条评论),这样 作者只会收到一条通知,而不是 N 条
  • 提交中立的 COMMENT 审查,而不是 REQUEST_CHANGES。由用户通过下面的后续命令决定如何处理。
后续命令

无需重复完整上下文即可使用:

  • "re-review" — 获取最新更改,仅审查新增或修改的 代码块,并呈现发现
  • "approve" — 添加建设性评论并批准 PR
  • "request changes" — 提交审查并请求更改,同时总结尚未解决的问题
自主模式

在没有人工参与的情况下运行(CI、GitHub Actions、机器人)时, 采用以下默认设置,而非上面的交互步骤:

  • 范围:仅限优先级 1 和 2。 不要评论 P3 问题。
  • 每个 PR 最多 5 条审查评论。 如果发现问题超过 5 个, 发布最关键的 5 个,并在审查摘要中注明其余问题的数量。
  • 直接发布——跳过预览步骤(第 3 步)。此时没有用户需要批准。
  • 提交为 COMMENT,绝不使用 REQUEST_CHANGES 或 APPROVE。
  • 如果没有发现 P1 问题,则不要发布任何审查。 对于自主审查器,沉默即表示认可。
  • 对同一 PR 的新提交,仅重新审查发生更改的代码块。

仓库级覆盖设置可以放宽或收紧这些默认值(例如,纳入 P2、提高或降低评论数量上限)。

注意: 要发布到 GitHub,必须安装并完成身份验证的 gh CLI。

资源

评论

支持 Markdown