ccg-ros2-workflow 2.2.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +211 -96
- package/README.zh-CN.md +256 -0
- package/dist/cli.mjs +15 -15
- package/dist/index.d.mts +59 -36
- package/dist/index.d.ts +59 -36
- package/dist/index.mjs +4 -4
- package/dist/shared/ccg-ros2-workflow.Bhm8c7P1.mjs +5154 -0
- package/package.json +31 -12
- package/templates/codex/AGENTS.md +348 -0
- package/templates/codex/agents/ccg-implement.toml +73 -0
- package/templates/codex/agents/ccg-research.toml +73 -0
- package/templates/codex/agents/ccg-review.toml +82 -0
- package/templates/codex/config.toml +21 -0
- package/templates/codex/hooks/ccg-workflow.py +253 -0
- package/templates/codex/hooks.json +15 -0
- package/templates/commands/agents/planner.md +97 -122
- package/templates/commands/agents/system-integrator.md +2 -2
- package/templates/commands/agents/team-architect.md +97 -0
- package/templates/commands/agents/team-qa.md +121 -0
- package/templates/commands/agents/team-reviewer.md +112 -0
- package/templates/commands/commit.md +30 -1
- package/templates/commands/context.md +332 -0
- package/templates/commands/go.md +206 -0
- package/templates/commands/init.md +1 -1
- package/templates/commands/spec-impl.md +41 -21
- package/templates/commands/spec-init.md +21 -27
- package/templates/commands/spec-plan.md +54 -21
- package/templates/commands/spec-research.md +78 -26
- package/templates/commands/spec-review.md +20 -16
- package/templates/{commands → commands-legacy}/analyze.md +1 -1
- package/templates/commands-legacy/backend.md +224 -0
- package/templates/commands-legacy/codex-exec.md +411 -0
- package/templates/{commands → commands-legacy}/debug.md +1 -1
- package/templates/commands-legacy/enhance.md +55 -0
- package/templates/{commands → commands-legacy}/feat.md +2 -2
- package/templates/commands-legacy/frontend.md +213 -0
- package/templates/{commands → commands-legacy}/optimize.md +1 -1
- package/templates/{commands → commands-legacy}/plan.md +1 -15
- package/templates/{commands → commands-legacy}/team-plan.md +1 -1
- package/templates/commands-legacy/team.md +475 -0
- package/templates/{commands → commands-legacy}/test.md +1 -1
- package/templates/commands-legacy/workflow.md +283 -0
- package/templates/engine/model-router.md +123 -0
- package/templates/engine/phase-guide.md +207 -0
- package/templates/engine/strategies/debug-investigate.md +169 -0
- package/templates/engine/strategies/deep-research.md +141 -0
- package/templates/engine/strategies/direct-fix.md +108 -0
- package/templates/engine/strategies/full-collaborate.md +389 -0
- package/templates/engine/strategies/git-action.md +43 -0
- package/templates/engine/strategies/guided-develop.md +282 -0
- package/templates/engine/strategies/optimize-measure.md +103 -0
- package/templates/engine/strategies/quick-implement.md +96 -0
- package/templates/engine/strategies/refactor-safely.md +180 -0
- package/templates/engine/strategies/review-audit.md +123 -0
- package/templates/hooks/session-start.js +100 -0
- package/templates/hooks/skill-router.js +144 -0
- package/templates/hooks/subagent-context.js +161 -0
- package/templates/hooks/task-utils.js +190 -0
- package/templates/hooks/workflow-state.js +55 -0
- package/templates/output-styles/abyss-command.md +56 -0
- package/templates/output-styles/abyss-concise.md +89 -0
- package/templates/output-styles/abyss-ritual.md +70 -0
- package/templates/output-styles/engineer-professional.md +20 -3
- package/templates/output-styles/laowang-engineer.md +2 -2
- package/templates/prompts/antigravity/analyzer.md +59 -0
- package/templates/prompts/antigravity/architect.md +55 -0
- package/templates/prompts/antigravity/builder.md +52 -0
- package/templates/prompts/antigravity/debugger.md +48 -0
- package/templates/prompts/antigravity/frontend.md +50 -0
- package/templates/prompts/antigravity/optimizer.md +40 -0
- package/templates/prompts/antigravity/reviewer.md +67 -0
- package/templates/prompts/antigravity/tester.md +39 -0
- package/templates/prompts/claude/debugger.md +1 -1
- package/templates/prompts/claude/reviewer.md +1 -1
- package/templates/prompts/codex/analyzer.md +8 -0
- package/templates/prompts/codex/architect.md +9 -1
- package/templates/prompts/codex/builder.md +61 -0
- package/templates/prompts/codex/debugger.md +9 -1
- package/templates/prompts/codex/optimizer.md +7 -0
- package/templates/prompts/codex/reviewer.md +7 -0
- package/templates/prompts/codex/tester.md +8 -1
- package/templates/prompts/gemini/analyzer.md +11 -3
- package/templates/prompts/gemini/architect.md +10 -2
- package/templates/prompts/gemini/debugger.md +8 -0
- package/templates/prompts/gemini/frontend.md +10 -2
- package/templates/prompts/gemini/optimizer.md +9 -2
- package/templates/prompts/gemini/reviewer.md +7 -0
- package/templates/prompts/gemini/tester.md +8 -1
- package/templates/rules/ccg-skill-routing.md +91 -0
- package/templates/rules/ccg-skills.md +65 -0
- package/templates/skills/SKILL.md +92 -0
- package/templates/skills/domains/ai/SKILL.md +34 -0
- package/templates/skills/domains/ai/agent-dev.md +242 -0
- package/templates/skills/domains/ai/llm-security.md +288 -0
- package/templates/skills/domains/ai/prompt-and-eval.md +279 -0
- package/templates/skills/domains/ai/rag-system.md +542 -0
- package/templates/skills/domains/architecture/SKILL.md +42 -0
- package/templates/skills/domains/architecture/api-design.md +225 -0
- package/templates/skills/domains/architecture/caching.md +299 -0
- package/templates/skills/domains/architecture/cloud-native.md +285 -0
- package/templates/skills/domains/architecture/message-queue.md +329 -0
- package/templates/skills/domains/architecture/security-arch.md +297 -0
- package/templates/skills/domains/data-engineering/SKILL.md +207 -0
- package/templates/skills/domains/development/SKILL.md +46 -0
- package/templates/skills/domains/development/cpp.md +369 -0
- package/templates/skills/domains/development/go.md +323 -0
- package/templates/skills/domains/development/java.md +277 -0
- package/templates/skills/domains/development/python.md +487 -0
- package/templates/skills/domains/development/rust.md +313 -0
- package/templates/skills/domains/development/shell.md +313 -0
- package/templates/skills/domains/development/typescript.md +277 -0
- package/templates/skills/domains/devops/SKILL.md +39 -0
- package/templates/skills/domains/devops/cost-optimization.md +272 -0
- package/templates/skills/domains/devops/database.md +217 -0
- package/templates/skills/domains/devops/devsecops.md +198 -0
- package/templates/skills/domains/devops/git-workflow.md +181 -0
- package/templates/skills/domains/devops/observability.md +280 -0
- package/templates/skills/domains/devops/performance.md +336 -0
- package/templates/skills/domains/devops/testing.md +283 -0
- package/templates/skills/domains/infrastructure/SKILL.md +200 -0
- package/templates/skills/domains/mobile/SKILL.md +224 -0
- package/templates/skills/domains/orchestration/SKILL.md +29 -0
- package/templates/skills/domains/orchestration/multi-agent.md +263 -0
- package/templates/skills/domains/ros2-control/SKILL.md +206 -0
- package/templates/skills/domains/ros2-hardware/SKILL.md +277 -0
- package/templates/skills/domains/ros2-manipulation/SKILL.md +237 -0
- package/templates/skills/domains/ros2-navigation/SKILL.md +196 -0
- package/templates/skills/domains/ros2-perception/SKILL.md +166 -0
- package/templates/skills/domains/ros2-upper-app/SKILL.md +50 -0
- package/templates/skills/domains/ros2-upper-app/launch-files.md +224 -0
- package/templates/skills/domains/ros2-upper-app/parameters.md +192 -0
- package/templates/skills/domains/ros2-upper-app/python-nodes.md +249 -0
- package/templates/skills/domains/ros2-upper-app/rviz-config.md +158 -0
- package/templates/skills/domains/ros2-upper-app/simulation.md +225 -0
- package/templates/skills/domains/security/SKILL.md +72 -0
- package/templates/skills/domains/security/blue-team.md +436 -0
- package/templates/skills/domains/security/code-audit.md +265 -0
- package/templates/skills/domains/security/pentest.md +226 -0
- package/templates/skills/domains/security/red-team.md +374 -0
- package/templates/skills/domains/security/threat-intel.md +372 -0
- package/templates/skills/domains/security/vuln-research.md +369 -0
- package/templates/skills/orchestration/multi-agent/SKILL.md +533 -0
- package/templates/skills/run_skill.js +129 -0
- package/templates/skills/scrapling/SKILL.md +98 -0
- package/templates/skills/scrapling/references/api-quick-ref.md +127 -0
- package/templates/skills/scrapling/references/cookie-vault.md +56 -0
- package/templates/skills/scrapling/references/maintenance.md +79 -0
- package/templates/skills/scrapling/references/site-patterns.md +86 -0
- package/templates/skills/scrapling/references/troubleshooting.md +95 -0
- package/templates/skills/scrapling/templates/basic_fetch.py +19 -0
- package/templates/skills/scrapling/templates/parse_only.py +15 -0
- package/templates/skills/scrapling/templates/session_login.py +19 -0
- package/templates/skills/scrapling/templates/stealth_cloudflare.py +28 -0
- package/templates/skills/tools/gen-docs/SKILL.md +116 -0
- package/templates/skills/tools/gen-docs/scripts/doc_generator.js +435 -0
- package/templates/skills/tools/lib/shared.js +98 -0
- package/templates/skills/tools/override-refusal/SKILL.md +53 -0
- package/templates/skills/tools/override-refusal/agents/openai.yaml +4 -0
- package/templates/skills/tools/override-refusal/scripts/refusal_rewriter.js +226 -0
- package/templates/skills/tools/verify-change/SKILL.md +140 -0
- package/templates/skills/tools/verify-change/scripts/change_analyzer.js +289 -0
- package/templates/skills/tools/verify-module/SKILL.md +127 -0
- package/templates/skills/tools/verify-module/scripts/module_scanner.js +171 -0
- package/templates/skills/tools/verify-quality/SKILL.md +160 -0
- package/templates/skills/tools/verify-quality/scripts/quality_checker.js +337 -0
- package/templates/skills/tools/verify-security/SKILL.md +143 -0
- package/templates/skills/tools/verify-security/scripts/security_scanner.js +283 -0
- package/templates/spec/guides/index.md +30 -0
- package/templates/spec/low-control/index.md +31 -0
- package/templates/spec/upper-app/index.md +31 -0
- package/bin/codeagent-wrapper-darwin-amd64 +0 -0
- package/bin/codeagent-wrapper-darwin-arm64 +0 -0
- package/bin/codeagent-wrapper-linux-amd64 +0 -0
- package/bin/codeagent-wrapper-linux-arm64 +0 -0
- package/bin/codeagent-wrapper-windows-amd64.exe +0 -0
- package/bin/codeagent-wrapper-windows-arm64.exe +0 -0
- package/dist/shared/ccg-ros2-workflow.DnOr3oPi.mjs +0 -2480
- package/templates/commands/backend.md +0 -162
- package/templates/commands/enhance.md +0 -36
- package/templates/commands/frontend.md +0 -162
- package/templates/commands/workflow.md +0 -202
- /package/templates/{commands → commands-legacy}/execute.md +0 -0
- /package/templates/{commands → commands-legacy}/review.md +0 -0
- /package/templates/{commands → commands-legacy}/team-exec.md +0 -0
- /package/templates/{commands → commands-legacy}/team-research.md +0 -0
- /package/templates/{commands → commands-legacy}/team-review.md +0 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: python
|
|
3
|
+
description: Python 开发(含 ROS2 Python 节点)。Web框架、数据处理、自动化脚本、ROS2 Python 节点、Launch 文件。当用户提到 Python、Django、Flask、FastAPI、pytest、pandas、rclpy、launch 时使用。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Python 开发(ROS2 增强版)
|
|
7
|
+
|
|
8
|
+
## ROS2 Python 开发上下文(上层应用核心)
|
|
9
|
+
|
|
10
|
+
ROS2 项目中,Python 节点承担**上层应用**职责:Launch 文件、参数配置、可视化、仿真、状态机编排。
|
|
11
|
+
|
|
12
|
+
### rclpy 核心模式
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
import rclpy
|
|
16
|
+
from rclpy.node import Node
|
|
17
|
+
from rclpy.qos import QoSProfile, ReliabilityPolicy, HistoryPolicy
|
|
18
|
+
from sensor_msgs.msg import LaserScan
|
|
19
|
+
from geometry_msgs.msg import Twist
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class NavigationNode(Node):
|
|
23
|
+
def __init__(self):
|
|
24
|
+
super().__init__('navigation_node')
|
|
25
|
+
|
|
26
|
+
# QoS 策略
|
|
27
|
+
sensor_qos = QoSProfile(
|
|
28
|
+
reliability=ReliabilityPolicy.BEST_EFFORT,
|
|
29
|
+
history=HistoryPolicy.KEEP_LAST,
|
|
30
|
+
depth=10
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# 订阅传感器
|
|
34
|
+
self.scan_sub = self.create_subscription(
|
|
35
|
+
LaserScan, '/scan', self.scan_callback, sensor_qos)
|
|
36
|
+
|
|
37
|
+
# 发布控制指令
|
|
38
|
+
self.cmd_pub = self.create_publisher(Twist, '/cmd_vel', 10)
|
|
39
|
+
|
|
40
|
+
# 参数声明
|
|
41
|
+
self.declare_parameter('max_speed', 1.0)
|
|
42
|
+
self.declare_parameter('safe_distance', 0.5)
|
|
43
|
+
|
|
44
|
+
def scan_callback(self, msg: LaserScan):
|
|
45
|
+
max_speed = self.get_parameter('max_speed').value
|
|
46
|
+
# 处理逻辑
|
|
47
|
+
cmd = Twist()
|
|
48
|
+
cmd.linear.x = max_speed
|
|
49
|
+
self.cmd_pub.publish(cmd)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def main():
|
|
53
|
+
rclpy.init()
|
|
54
|
+
node = NavigationNode()
|
|
55
|
+
try:
|
|
56
|
+
rclpy.spin(node)
|
|
57
|
+
finally:
|
|
58
|
+
node.destroy_node()
|
|
59
|
+
rclpy.shutdown()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
if __name__ == '__main__':
|
|
63
|
+
main()
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Launch 文件标准模板
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
# launch/navigation.launch.py
|
|
70
|
+
from launch import LaunchDescription
|
|
71
|
+
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
|
|
72
|
+
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
|
73
|
+
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
|
|
74
|
+
from launch_ros.actions import Node
|
|
75
|
+
from launch_ros.substitutions import FindPackageShare
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def generate_launch_description():
|
|
79
|
+
use_sim_time = LaunchConfiguration('use_sim_time', default='false')
|
|
80
|
+
params_file = PathJoinSubstitution([
|
|
81
|
+
FindPackageShare('my_robot'), 'config', 'params.yaml'
|
|
82
|
+
])
|
|
83
|
+
|
|
84
|
+
return LaunchDescription([
|
|
85
|
+
DeclareLaunchArgument(
|
|
86
|
+
'use_sim_time',
|
|
87
|
+
default_value='false',
|
|
88
|
+
description='Use simulation clock'),
|
|
89
|
+
|
|
90
|
+
Node(
|
|
91
|
+
package='my_robot',
|
|
92
|
+
executable='navigation_node',
|
|
93
|
+
name='navigation',
|
|
94
|
+
parameters=[params_file, {'use_sim_time': use_sim_time}],
|
|
95
|
+
remappings=[('/scan', '/lidar/scan')],
|
|
96
|
+
output='screen',
|
|
97
|
+
),
|
|
98
|
+
|
|
99
|
+
IncludeLaunchDescription(
|
|
100
|
+
PythonLaunchDescriptionSource([
|
|
101
|
+
FindPackageShare('nav2_bringup'),
|
|
102
|
+
'/launch/navigation_launch.py'
|
|
103
|
+
])
|
|
104
|
+
),
|
|
105
|
+
])
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### setup.py 标准模板
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from setuptools import setup
|
|
112
|
+
import os
|
|
113
|
+
from glob import glob
|
|
114
|
+
|
|
115
|
+
package_name = 'my_robot'
|
|
116
|
+
|
|
117
|
+
setup(
|
|
118
|
+
name=package_name,
|
|
119
|
+
version='0.1.0',
|
|
120
|
+
packages=[package_name],
|
|
121
|
+
data_files=[
|
|
122
|
+
('share/ament_index/resource_index/packages',
|
|
123
|
+
['resource/' + package_name]),
|
|
124
|
+
('share/' + package_name, ['package.xml']),
|
|
125
|
+
(os.path.join('share', package_name, 'launch'),
|
|
126
|
+
glob('launch/*.launch.py')),
|
|
127
|
+
(os.path.join('share', package_name, 'config'),
|
|
128
|
+
glob('config/*.yaml')),
|
|
129
|
+
(os.path.join('share', package_name, 'rviz'),
|
|
130
|
+
glob('rviz/*.rviz')),
|
|
131
|
+
],
|
|
132
|
+
install_requires=['setuptools'],
|
|
133
|
+
zip_safe=True,
|
|
134
|
+
entry_points={
|
|
135
|
+
'console_scripts': [
|
|
136
|
+
'navigation_node = my_robot.navigation_node:main',
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 参数 YAML 配置
|
|
143
|
+
|
|
144
|
+
```yaml
|
|
145
|
+
# config/params.yaml
|
|
146
|
+
navigation:
|
|
147
|
+
ros__parameters:
|
|
148
|
+
max_speed: 1.0
|
|
149
|
+
safe_distance: 0.5
|
|
150
|
+
update_rate: 50.0
|
|
151
|
+
frame_id: "base_link"
|
|
152
|
+
use_sim_time: false
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### ROS2 Python 测试 (launch_testing)
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
import unittest
|
|
159
|
+
import launch_testing
|
|
160
|
+
import pytest
|
|
161
|
+
from launch import LaunchDescription
|
|
162
|
+
from launch_ros.actions import Node
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@pytest.mark.launch_test
|
|
166
|
+
def generate_test_description():
|
|
167
|
+
return LaunchDescription([
|
|
168
|
+
Node(
|
|
169
|
+
package='my_robot',
|
|
170
|
+
executable='navigation_node',
|
|
171
|
+
),
|
|
172
|
+
launch_testing.actions.ReadyToTest(),
|
|
173
|
+
])
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class TestNavigation(unittest.TestCase):
|
|
177
|
+
def test_node_starts(self, proc_info):
|
|
178
|
+
proc_info.assertWaitForStartup(timeout=10)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 仿真集成(Gazebo)
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
# launch/simulation.launch.py
|
|
185
|
+
def generate_launch_description():
|
|
186
|
+
return LaunchDescription([
|
|
187
|
+
IncludeLaunchDescription(
|
|
188
|
+
PythonLaunchDescriptionSource([
|
|
189
|
+
FindPackageShare('gazebo_ros'),
|
|
190
|
+
'/launch/gazebo.launch.py'
|
|
191
|
+
])
|
|
192
|
+
),
|
|
193
|
+
Node(
|
|
194
|
+
package='robot_state_publisher',
|
|
195
|
+
executable='robot_state_publisher',
|
|
196
|
+
parameters=[{'robot_description': robot_description}],
|
|
197
|
+
),
|
|
198
|
+
])
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 通用 Python 知识(以下保持原样)
|
|
204
|
+
|
|
205
|
+
# 📜 符箓秘典 · Python
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
## Web 框架
|
|
209
|
+
|
|
210
|
+
### FastAPI (推荐)
|
|
211
|
+
```python
|
|
212
|
+
from fastapi import FastAPI, HTTPException, Depends
|
|
213
|
+
from pydantic import BaseModel
|
|
214
|
+
from typing import Optional
|
|
215
|
+
|
|
216
|
+
app = FastAPI()
|
|
217
|
+
|
|
218
|
+
class User(BaseModel):
|
|
219
|
+
name: str
|
|
220
|
+
email: str
|
|
221
|
+
age: Optional[int] = None
|
|
222
|
+
|
|
223
|
+
@app.get("/users/{user_id}")
|
|
224
|
+
async def get_user(user_id: int):
|
|
225
|
+
return {"user_id": user_id}
|
|
226
|
+
|
|
227
|
+
@app.post("/users")
|
|
228
|
+
async def create_user(user: User):
|
|
229
|
+
return user
|
|
230
|
+
|
|
231
|
+
# 依赖注入
|
|
232
|
+
async def get_db():
|
|
233
|
+
db = Database()
|
|
234
|
+
try:
|
|
235
|
+
yield db
|
|
236
|
+
finally:
|
|
237
|
+
await db.close()
|
|
238
|
+
|
|
239
|
+
@app.get("/items")
|
|
240
|
+
async def get_items(db = Depends(get_db)):
|
|
241
|
+
return await db.fetch_all("SELECT * FROM items")
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Flask
|
|
245
|
+
```python
|
|
246
|
+
from flask import Flask, request, jsonify
|
|
247
|
+
|
|
248
|
+
app = Flask(__name__)
|
|
249
|
+
|
|
250
|
+
@app.route('/api/users', methods=['GET', 'POST'])
|
|
251
|
+
def users():
|
|
252
|
+
if request.method == 'POST':
|
|
253
|
+
data = request.json
|
|
254
|
+
return jsonify(data), 201
|
|
255
|
+
return jsonify([])
|
|
256
|
+
|
|
257
|
+
@app.errorhandler(404)
|
|
258
|
+
def not_found(e):
|
|
259
|
+
return jsonify(error="Not found"), 404
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Django
|
|
263
|
+
```python
|
|
264
|
+
# models.py
|
|
265
|
+
from django.db import models
|
|
266
|
+
|
|
267
|
+
class User(models.Model):
|
|
268
|
+
name = models.CharField(max_length=100)
|
|
269
|
+
email = models.EmailField(unique=True)
|
|
270
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
271
|
+
|
|
272
|
+
# views.py
|
|
273
|
+
from django.http import JsonResponse
|
|
274
|
+
from django.views import View
|
|
275
|
+
|
|
276
|
+
class UserView(View):
|
|
277
|
+
def get(self, request, user_id):
|
|
278
|
+
user = User.objects.get(id=user_id)
|
|
279
|
+
return JsonResponse({'name': user.name})
|
|
280
|
+
|
|
281
|
+
# urls.py
|
|
282
|
+
urlpatterns = [
|
|
283
|
+
path('users/<int:user_id>/', UserView.as_view()),
|
|
284
|
+
]
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## 异步编程
|
|
288
|
+
|
|
289
|
+
```python
|
|
290
|
+
import asyncio
|
|
291
|
+
import aiohttp
|
|
292
|
+
|
|
293
|
+
async def fetch(url: str) -> str:
|
|
294
|
+
async with aiohttp.ClientSession() as session:
|
|
295
|
+
async with session.get(url) as response:
|
|
296
|
+
return await response.text()
|
|
297
|
+
|
|
298
|
+
async def fetch_all(urls: list[str]) -> list[str]:
|
|
299
|
+
tasks = [fetch(url) for url in urls]
|
|
300
|
+
return await asyncio.gather(*tasks)
|
|
301
|
+
|
|
302
|
+
# 运行
|
|
303
|
+
asyncio.run(fetch_all(['http://example.com', 'http://example.org']))
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## 数据处理
|
|
307
|
+
|
|
308
|
+
### Pandas
|
|
309
|
+
```python
|
|
310
|
+
import pandas as pd
|
|
311
|
+
|
|
312
|
+
# 读取数据
|
|
313
|
+
df = pd.read_csv('data.csv')
|
|
314
|
+
df = pd.read_json('data.json')
|
|
315
|
+
|
|
316
|
+
# 数据清洗
|
|
317
|
+
df = df.dropna()
|
|
318
|
+
df = df.drop_duplicates()
|
|
319
|
+
df['column'] = df['column'].str.strip()
|
|
320
|
+
|
|
321
|
+
# 数据转换
|
|
322
|
+
df['date'] = pd.to_datetime(df['date'])
|
|
323
|
+
df['category'] = df['category'].astype('category')
|
|
324
|
+
|
|
325
|
+
# 聚合分析
|
|
326
|
+
result = df.groupby('category').agg({
|
|
327
|
+
'value': ['sum', 'mean', 'count']
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
# 导出
|
|
331
|
+
df.to_csv('output.csv', index=False)
|
|
332
|
+
df.to_json('output.json', orient='records')
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## 测试
|
|
336
|
+
|
|
337
|
+
### pytest
|
|
338
|
+
```python
|
|
339
|
+
import pytest
|
|
340
|
+
from myapp import calculate, UserService
|
|
341
|
+
|
|
342
|
+
# 基础测试
|
|
343
|
+
def test_add():
|
|
344
|
+
assert calculate.add(1, 2) == 3
|
|
345
|
+
|
|
346
|
+
# 参数化
|
|
347
|
+
@pytest.mark.parametrize("a,b,expected", [
|
|
348
|
+
(1, 2, 3),
|
|
349
|
+
(0, 0, 0),
|
|
350
|
+
(-1, 1, 0),
|
|
351
|
+
])
|
|
352
|
+
def test_add_params(a, b, expected):
|
|
353
|
+
assert calculate.add(a, b) == expected
|
|
354
|
+
|
|
355
|
+
# Fixture
|
|
356
|
+
@pytest.fixture
|
|
357
|
+
def user_service():
|
|
358
|
+
service = UserService()
|
|
359
|
+
yield service
|
|
360
|
+
service.cleanup()
|
|
361
|
+
|
|
362
|
+
def test_create_user(user_service):
|
|
363
|
+
user = user_service.create("test")
|
|
364
|
+
assert user.name == "test"
|
|
365
|
+
|
|
366
|
+
# Mock
|
|
367
|
+
from unittest.mock import Mock, patch
|
|
368
|
+
|
|
369
|
+
@patch('myapp.requests.get')
|
|
370
|
+
def test_fetch(mock_get):
|
|
371
|
+
mock_get.return_value.json.return_value = {"id": 1}
|
|
372
|
+
result = fetch_user(1)
|
|
373
|
+
assert result["id"] == 1
|
|
374
|
+
|
|
375
|
+
# 异步测试
|
|
376
|
+
@pytest.mark.asyncio
|
|
377
|
+
async def test_async_fetch():
|
|
378
|
+
result = await async_fetch()
|
|
379
|
+
assert result is not None
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### 运行测试
|
|
383
|
+
```bash
|
|
384
|
+
pytest # 运行所有
|
|
385
|
+
pytest test_file.py # 指定文件
|
|
386
|
+
pytest -k "test_add" # 匹配名称
|
|
387
|
+
pytest -v # 详细输出
|
|
388
|
+
pytest --cov=myapp # 覆盖率
|
|
389
|
+
pytest -x # 失败即停
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## CLI 工具
|
|
393
|
+
|
|
394
|
+
### Typer (推荐)
|
|
395
|
+
```python
|
|
396
|
+
import typer
|
|
397
|
+
|
|
398
|
+
app = typer.Typer()
|
|
399
|
+
|
|
400
|
+
@app.command()
|
|
401
|
+
def hello(name: str, count: int = 1):
|
|
402
|
+
"""Say hello NAME, COUNT times."""
|
|
403
|
+
for _ in range(count):
|
|
404
|
+
typer.echo(f"Hello {name}!")
|
|
405
|
+
|
|
406
|
+
@app.command()
|
|
407
|
+
def goodbye(name: str, formal: bool = False):
|
|
408
|
+
if formal:
|
|
409
|
+
typer.echo(f"Goodbye Ms. {name}. Have a good day.")
|
|
410
|
+
else:
|
|
411
|
+
typer.echo(f"Bye {name}!")
|
|
412
|
+
|
|
413
|
+
if __name__ == "__main__":
|
|
414
|
+
app()
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### argparse
|
|
418
|
+
```python
|
|
419
|
+
import argparse
|
|
420
|
+
|
|
421
|
+
parser = argparse.ArgumentParser(description='My CLI tool')
|
|
422
|
+
parser.add_argument('input', help='Input file')
|
|
423
|
+
parser.add_argument('-o', '--output', default='output.txt')
|
|
424
|
+
parser.add_argument('-v', '--verbose', action='store_true')
|
|
425
|
+
|
|
426
|
+
args = parser.parse_args()
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## 项目结构
|
|
430
|
+
|
|
431
|
+
```
|
|
432
|
+
myproject/
|
|
433
|
+
├── pyproject.toml # 项目配置
|
|
434
|
+
├── README.md
|
|
435
|
+
├── src/
|
|
436
|
+
│ └── myproject/
|
|
437
|
+
│ ├── __init__.py
|
|
438
|
+
│ ├── main.py
|
|
439
|
+
│ ├── models.py
|
|
440
|
+
│ └── utils.py
|
|
441
|
+
├── tests/
|
|
442
|
+
│ ├── __init__.py
|
|
443
|
+
│ ├── conftest.py
|
|
444
|
+
│ └── test_main.py
|
|
445
|
+
└── scripts/
|
|
446
|
+
└── run.py
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### pyproject.toml
|
|
450
|
+
```toml
|
|
451
|
+
[project]
|
|
452
|
+
name = "myproject"
|
|
453
|
+
version = "0.1.0"
|
|
454
|
+
dependencies = [
|
|
455
|
+
"fastapi>=0.100.0",
|
|
456
|
+
"uvicorn>=0.23.0",
|
|
457
|
+
]
|
|
458
|
+
|
|
459
|
+
[project.optional-dependencies]
|
|
460
|
+
dev = [
|
|
461
|
+
"pytest>=7.0.0",
|
|
462
|
+
"pytest-cov>=4.0.0",
|
|
463
|
+
]
|
|
464
|
+
|
|
465
|
+
[tool.pytest.ini_options]
|
|
466
|
+
testpaths = ["tests"]
|
|
467
|
+
|
|
468
|
+
[tool.ruff]
|
|
469
|
+
line-length = 120
|
|
470
|
+
select = ["E", "F", "I"]
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
## 常用库
|
|
474
|
+
|
|
475
|
+
| 库 | 用途 |
|
|
476
|
+
|---|------|
|
|
477
|
+
| requests/httpx | HTTP 客户端 |
|
|
478
|
+
| aiohttp | 异步 HTTP |
|
|
479
|
+
| SQLAlchemy | ORM |
|
|
480
|
+
| Pydantic | 数据验证 |
|
|
481
|
+
| Click/Typer | CLI |
|
|
482
|
+
| pytest | 测试 |
|
|
483
|
+
| pandas | 数据处理 |
|
|
484
|
+
| loguru | 日志 |
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|