代码规范¶
概述¶
SQLAlchemy CouchDB 方言遵循严格的代码规范,确保代码质量、可读性和可维护性。
Python 代码规范¶
PEP 8 合规¶
遵循 PEP 8 Python 编码规范:
# ✅ 正确:使用 4 个空格缩进
def example_function():
if True:
print("正确缩进")
# ❌ 错误:使用 Tab
def example_function():
if True:
print("错误缩进")
行长度¶
- 最大行长度: 88 字符(Black 默认)
- 长行处理: 使用括号或反斜杠
# ✅ 正确:使用括号
result = some_function(
arg1='value1',
arg2='value2',
arg3='value3'
)
# ✅ 正确:字符串拼接
long_string = (
"这是一个很长的字符串 "
"需要跨多行显示"
)
导入规范¶
# ✅ 标准库
import os
import sys
from datetime import datetime
# ✅ 第三方库
import httpx
import pytest
from sqlalchemy import text
# ✅ 本地模块
from sqlalchemy_couchdb.client import SyncCouchDBClient
from sqlalchemy_couchdb.compiler import CouchDBCompiler
# ❌ 避免:混乱的导入
from sqlalchemy import *
import os, sys
函数和类命名¶
# ✅ 函数:小写下划线
def get_user_info():
pass
def fetch_document():
pass
# ✅ 类:PascalCase
class CouchDBDialect:
pass
class AsyncCouchDBClient:
pass
# ✅ 常量:大写下划线
DEFAULT_TIMEOUT = 30
MAX_RETRIES = 3
# ✅ 私有方法:前导下划线
class Example:
def _private_method(self):
pass
# ✅ 私有属性:前导下划线
class Example:
def __init__(self):
self._private_attr = None
变量命名¶
# ✅ 描述性名称
user_id = 'user:123'
document_count = 0
is_active = True
# ❌ 模糊名称
x = 'user:123'
c = 0
flag = True
文档字符串¶
使用 Google 风格¶
def compile_select(
self,
select: Select,
**kwargs: Any
) -> dict:
"""
编译 SELECT 语句为 Mango Query。
此方法将 SQLAlchemy Select 对象转换为 CouchDB Mango Query,
支持 WHERE、ORDER BY、LIMIT 等子句。
参数:
select: SQLAlchemy Select 对象
**kwargs: 其他编译参数
返回:
包含 selector、fields、sort 等的字典
异常:
CompileError: 编译失败时抛出
示例:
>>> compiler = CouchDBCompiler(dialect, None)
>>> sql = text("SELECT * FROM users WHERE age > 25")
>>> mango = compiler.compile_select(sql)
>>> print(mango)
{
'selector': {'age': {'$gt': 25}},
'fields': ['_id', 'name', 'age']
}
注意:
此方法会自动创建所需的索引
"""
pass
类文档¶
class CouchDBCompiler:
"""
SQL → Mango Query 编译器。
负责将 SQLAlchemy SQL 语句转换为 CouchDB Mango Query,
支持 SELECT、INSERT、UPDATE、DELETE 等操作。
属性:
dialect: 关联的方言实例
client: CouchDB 客户端
"""
def __init__(self, dialect, statement, **kwargs):
"""初始化编译器。"""
self.dialect = dialect
self.statement = statement
类型提示¶
必需的类型提示¶
from typing import Dict, List, Optional, Union, Any
def process_value(
value: Union[str, int],
default: Optional[str] = None
) -> Dict[str, Any]:
"""
处理输入值。
参数:
value: 要处理的值,可以是字符串或整数
default: 默认值,如果输入为空则使用
返回:
包含处理结果的字典
"""
pass
复杂类型¶
from typing import Protocol, TypeVar
T = TypeVar('T')
# Protocol 用于定义接口
class ClientProtocol(Protocol):
def find(self, selector: dict) -> List[dict]:
"""执行查询。"""
...
# 泛型
def process_list(items: List[T]) -> List[T]:
"""处理列表。"""
return items
# 可调用类型
from typing import Callable
Callback = Callable[[dict], None]
def process_with_callback(data: dict, callback: Callback) -> None:
"""使用回调处理数据。"""
callback(data)
代码结构¶
类结构¶
class ExampleClass:
"""示例类。"""
# 类变量
default_value = 100
def __init__(self, value: int):
"""初始化。"""
self.value = value
self._private = None
# 公共方法
def public_method(self) -> str:
"""公共方法。"""
return f"Value: {self.value}"
# 私有方法
def _private_method(self) -> None:
"""私有方法。"""
pass
# 静态方法
@staticmethod
def static_method() -> str:
"""静态方法。"""
return "Static"
# 类方法
@classmethod
def from_string(cls, s: str) -> 'ExampleClass':
"""从字符串创建实例。"""
return cls(int(s))
# 属性
@property
def doubled(self) -> int:
"""只读属性。"""
return self.value * 2
模块结构¶
"""
模块说明。
此模块提供 ... 功能。
"""
from __future__ import annotations
from typing import List, Optional
import logging
# 日志配置
logger = logging.getLogger(__name__)
# 常量
DEFAULT_TIMEOUT = 30
# 异常
class CustomError(Exception):
"""自定义异常。"""
pass
# 辅助函数
def helper_function(arg: str) -> bool:
"""辅助函数。"""
return True
# 主要类
class MainClass:
"""主要类。"""
pass
# 公开 API
__all__ = [
'MainClass',
'helper_function',
'CustomError',
]
错误处理¶
异常使用¶
# ✅ 特定异常
from sqlalchemy_couchdb.exceptions import CouchDBError
try:
result = client.get(doc_id)
except NotFoundError:
logger.warning("文档不存在: %s", doc_id)
return None
except DocumentConflictError:
logger.error("文档版本冲突: %s", doc_id)
raise
except CouchDBError as e:
logger.error("CouchDB 错误: %s", e)
raise
except Exception as e:
logger.exception("未预期错误")
raise
# ❌ 通用异常捕获
try:
result = client.get(doc_id)
except Exception:
# 太宽泛
pass
清理资源¶
# ✅ 使用上下文管理器
def process_data(data: list) -> list:
"""处理数据。"""
with open('output.txt', 'w') as f:
for item in data:
f.write(f"{item}\n")
# ✅ 显式清理
client = None
try:
client = SyncCouchDBClient(...)
# 使用 client
finally:
if client:
client.close()
# ❌ 资源泄露
client = SyncCouchDBClient(...)
# 没有关闭客户端
测试规范¶
测试文件命名¶
测试类和方法¶
import pytest
from sqlalchemy import text
class TestCouchDBCompiler:
"""编译器测试。"""
@pytest.fixture
def compiler(self, dialect):
"""创建编译器实例。"""
return CouchDBCompiler(dialect, None)
def test_simple_select(self, compiler):
"""简单 SELECT 测试。"""
# Arrange
sql = text("SELECT * FROM users WHERE type = 'user'")
# Act
mango = compiler.visit_select(sql)
# Assert
assert mango['selector'] == {"type": {"$eq": "user"}}
assert 'fields' in mango
assert 'limit' in mango
测试注释¶
def test_update_with_conflict(engine):
"""测试更新时的乐观锁冲突。"""
# 此测试验证:当文档被外部修改后,
# UPDATE 操作正确抛出 DocumentConflictError
pass
def test_concurrent_queries(async_engine):
"""测试并发查询。"""
# 注意:此测试需要真实的异步环境
pass
性能优化¶
避免性能陷阱¶
# ✅ 使用局部变量
def process_data(data):
result = []
append = result.append # 局部变量,加速
for item in data:
append(item)
return result
# ❌ 频繁属性访问
def process_data_slow(data):
result = []
for item in data:
result.append(item) # 每次都访问 result
return result
# ✅ 预计算值
def calculate_values(items):
# 预计算常用值
count = len(items)
sum_value = sum(item.value for item in items)
avg_value = sum_value / count if count > 0 else 0
return {'count': count, 'avg': avg_value}
内存优化¶
# ✅ 生成器(惰性求值)
def process_large_dataset(data):
for item in data:
yield process_item(item)
# ❌ 列表(立即求值)
def process_large_dataset_slow(data):
return [process_item(item) for item in data]
# ✅ 分块处理
def process_in_chunks(data, chunk_size=1000):
for i in range(0, len(data), chunk_size):
chunk = data[i:i + chunk_size]
yield process_chunk(chunk)
安全规范¶
输入验证¶
def validate_doc_id(doc_id: str) -> str:
"""验证文档 ID。"""
if not doc_id:
raise ValueError("文档 ID 不能为空")
if ':' not in doc_id:
raise ValueError("文档 ID 必须包含 ':'")
# 长度限制
if len(doc_id) > 256:
raise ValueError("文档 ID 过长")
return doc_id
参数化查询¶
# ✅ 始终使用参数化
conn.execute(
text("SELECT * FROM users WHERE age > :age"),
{"age": 25}
)
# ❌ 字符串拼接(SQL 注入风险)
age = 25
conn.execute(
text(f"SELECT * FROM users WHERE age > {age}")
)
敏感信息¶
Git 提交规范¶
提交信息格式¶
类型:
- feat: 新功能
- fix: 修复
- docs: 文档更新
- style: 代码格式
- refactor: 重构
- test: 测试
- chore: 构建/工具
示例:
代码审查检查表¶
功能正确性¶
- 代码完成所需功能
- 包含单元测试
- 通过所有测试
- 异常处理完整
代码质量¶
- 遵循 PEP 8
- 类型提示完整
- 文档字符串清晰
- 无死代码
性能¶
- 无明显性能问题
- 资源正确释放
- 避免不必要的计算
安全¶
- 无安全漏洞
- 参数化查询
- 输入验证
- 无硬编码敏感信息
工具配置¶
Black (代码格式化)¶
isort (导入排序)¶
mypy (类型检查)¶
# mypy.ini
[mypy]
python_version = 3.11
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True
[mypy-sqlalchemy_couchdb.*]
ignore_missing_imports = True
pytest (测试)¶
# pytest.ini
[tool:pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = -v --strict-markers
markers =
unit: 单元测试
integration: 集成测试
slow: 慢速测试