{"id":"e3f2646f-d19d-4d88-bc5f-a178dcd0d48c","shortId":"VRfrwH","kind":"skill","title":"python-project","tagline":"Modern Python project architecture guide for 2025. Use when creating Python projects (APIs, CLI, data pipelines). Covers uv, Ruff, Pydantic, FastAPI, and async patterns.","description":"# Python Project Architecture\n\n## Core Principles\n\n- **Type hints everywhere** — Pydantic for runtime, mypy for static\n- **uv for everything** — Package management, virtualenv, Python version\n- **Ruff only** — Replace Flake8 + Black + isort with single tool\n- **src layout** — All code under `src/` directory\n- **pyproject.toml only** — No setup.py, no requirements.txt\n- **Async all the way** — Once async, stay async through call chain\n- **No backwards compatibility** — Delete, don't deprecate. Change directly\n- **LiteLLM for LLM APIs** — Use LiteLLM proxy for all LLM integrations\n\n---\n\n## No Backwards Compatibility\n\n> **Delete unused code. Change directly. No compatibility layers.**\n\n```python\n# ❌ BAD: Deprecated decorator kept around\nimport warnings\n\ndef old_function():\n    warnings.warn(\"Use new_function instead\", DeprecationWarning)\n    return new_function()\n\n# ❌ BAD: Alias for renamed functions\nnew_name = old_name  # \"for backwards compatibility\"\n\n# ❌ BAD: Unused parameters with underscore\ndef process(_legacy_param, data):\n    ...\n\n# ❌ BAD: Version checking for old behavior\nif version < \"2.0\":\n    # old behavior\n    ...\n\n# ✅ GOOD: Just delete and update all usages\ndef new_function():\n    ...\n# Then: Find & replace all old_function → new_function\n\n# ✅ GOOD: Remove unused parameters entirely\ndef process(data):\n    ...\n```\n\n---\n\n## LiteLLM for LLM APIs\n\n> **Use LiteLLM proxy. Don't call provider APIs directly.**\n\n```python\n# src/myapp/llm.py\nfrom openai import AsyncOpenAI\n\nfrom myapp.config import settings\n\n# Connect to LiteLLM proxy using OpenAI SDK\nclient = AsyncOpenAI(\n    base_url=settings.litellm_url,  # \"http://localhost:4000\"\n    api_key=settings.litellm_api_key,\n)\n\n\nasync def complete(prompt: str, model: str = \"gpt-4o\") -> str:\n    \"\"\"Call any LLM through LiteLLM proxy.\"\"\"\n    response = await client.chat.completions.create(\n        model=model,  # \"gpt-4o\", \"claude-3-opus\", \"gemini-pro\", etc.\n        messages=[{\"role\": \"user\", \"content\": prompt}],\n    )\n    return response.choices[0].message.content or \"\"\n```\n\n---\n\n## Quick Start\n\n### 1. Initialize Project\n\n```bash\n# Install uv (if not installed)\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n\n# Create new project\nuv init myapp\ncd myapp\n\n# Set Python version\necho \"3.12\" > .python-version\n\n# Add dependencies\nuv add fastapi uvicorn pydantic sqlalchemy httpx\nuv add --dev pytest pytest-asyncio ruff mypy\n```\n\n### 2. Apply Tech Stack\n\n| Layer | Recommendation |\n|-------|----------------|\n| Package Manager | uv |\n| Linting + Format | Ruff |\n| Type Checking | mypy |\n| Validation | Pydantic v2 |\n| Web Framework | FastAPI |\n| Database | SQLAlchemy 2.0 + asyncpg |\n| HTTP Client | httpx |\n| Testing | pytest + pytest-asyncio |\n| Logging | structlog |\n\n### Version Strategy\n\n> **Always use latest. Never pin in templates.**\n\n```toml\n[project]\ndependencies = [\n    \"fastapi\",      # uv resolves to latest\n    \"pydantic\",\n    \"sqlalchemy\",\n]\n```\n\n- `uv add` fetches latest compatible versions\n- `uv.lock` ensures reproducible builds\n- `uv sync` installs exact locked versions\n\n### 3. Use Standard Structure (src layout)\n\n```\nmyapp/\n├── pyproject.toml         # Single config file\n├── uv.lock                # Lock file (commit this)\n├── .python-version        # Python version for uv\n├── src/\n│   └── myapp/\n│       ├── __init__.py\n│       ├── __main__.py    # Entry point\n│       ├── main.py        # FastAPI app\n│       ├── config.py      # Pydantic Settings\n│       ├── models/        # Pydantic models\n│       │   ├── __init__.py\n│       │   └── user.py\n│       ├── services/      # Business logic\n│       │   ├── __init__.py\n│       │   └── user.py\n│       ├── repositories/  # Data access\n│       │   ├── __init__.py\n│       │   └── user.py\n│       ├── api/           # HTTP layer\n│       │   ├── __init__.py\n│       │   ├── deps.py    # Dependencies\n│       │   └── routes/\n│       │       ├── __init__.py\n│       │       └── user.py\n│       └── core/          # Shared utilities\n│           ├── __init__.py\n│           ├── exceptions.py\n│           └── logging.py\n├── tests/\n│   ├── __init__.py\n│   ├── conftest.py        # Fixtures\n│   └── test_user.py\n└── Makefile\n```\n\n---\n\n## Architecture Layers\n\n### main.py — FastAPI Application\n\n```python\n# src/myapp/main.py\nfrom contextlib import asynccontextmanager\n\nfrom fastapi import FastAPI\n\nfrom myapp.api.routes import router\nfrom myapp.config import settings\nfrom myapp.core.logging import setup_logging\nfrom myapp.db import engine\n\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    # Startup\n    setup_logging()\n    yield\n    # Shutdown\n    await engine.dispose()\n\n\napp = FastAPI(\n    title=settings.app_name,\n    lifespan=lifespan,\n)\n\napp.include_router(router, prefix=\"/api/v1\")\n\n\n@app.get(\"/health\")\nasync def health():\n    return {\"status\": \"ok\"}\n```\n\n### config.py — Pydantic Settings\n\n```python\n# src/myapp/config.py\nfrom pydantic_settings import BaseSettings, SettingsConfigDict\n\n\nclass Settings(BaseSettings):\n    model_config = SettingsConfigDict(\n        env_file=\".env\",\n        env_file_encoding=\"utf-8\",\n    )\n\n    app_name: str = \"myapp\"\n    debug: bool = False\n\n    # Database\n    database_url: str = \"postgresql+asyncpg://localhost/myapp\"\n\n    # LiteLLM\n    litellm_url: str = \"http://localhost:4000\"\n    litellm_api_key: str = \"\"\n\n\nsettings = Settings()\n```\n\n### models/ — Pydantic Models\n\n```python\n# src/myapp/models/user.py\nfrom datetime import datetime\nfrom uuid import UUID\n\nfrom pydantic import BaseModel, EmailStr, Field\n\n\nclass UserBase(BaseModel):\n    email: EmailStr\n    name: str = Field(min_length=2, max_length=100)\n\n\nclass UserCreate(UserBase):\n    pass\n\n\nclass UserUpdate(BaseModel):\n    email: EmailStr | None = None\n    name: str | None = Field(default=None, min_length=2, max_length=100)\n\n\nclass User(UserBase):\n    id: UUID\n    created_at: datetime\n    updated_at: datetime\n\n    model_config = {\"from_attributes\": True}\n```\n\n### services/ — Business Logic\n\n```python\n# src/myapp/services/user.py\nfrom uuid import UUID\n\nfrom myapp.core.exceptions import NotFoundError, ConflictError\nfrom myapp.models.user import User, UserCreate, UserUpdate\nfrom myapp.repositories.user import UserRepository\n\n\nclass UserService:\n    def __init__(self, repo: UserRepository):\n        self.repo = repo\n\n    async def get(self, id: UUID) -> User:\n        user = await self.repo.get(id)\n        if not user:\n            raise NotFoundError(\"user\", str(id))\n        return user\n\n    async def create(self, data: UserCreate) -> User:\n        existing = await self.repo.get_by_email(data.email)\n        if existing:\n            raise ConflictError(\"email already exists\")\n        return await self.repo.create(data)\n\n    async def update(self, id: UUID, data: UserUpdate) -> User:\n        user = await self.get(id)\n        return await self.repo.update(user, data)\n\n    async def delete(self, id: UUID) -> None:\n        user = await self.get(id)\n        await self.repo.delete(user)\n```\n\n### api/routes/ — HTTP Handlers\n\n```python\n# src/myapp/api/routes/user.py\nfrom uuid import UUID\n\nfrom fastapi import APIRouter, Depends, status\n\nfrom myapp.api.deps import get_user_service\nfrom myapp.models.user import User, UserCreate, UserUpdate\nfrom myapp.services.user import UserService\n\nrouter = APIRouter(prefix=\"/users\", tags=[\"users\"])\n\n\n@router.get(\"/{id}\", response_model=User)\nasync def get_user(\n    id: UUID,\n    service: UserService = Depends(get_user_service),\n):\n    return await service.get(id)\n\n\n@router.post(\"\", response_model=User, status_code=status.HTTP_201_CREATED)\nasync def create_user(\n    data: UserCreate,\n    service: UserService = Depends(get_user_service),\n):\n    return await service.create(data)\n\n\n@router.patch(\"/{id}\", response_model=User)\nasync def update_user(\n    id: UUID,\n    data: UserUpdate,\n    service: UserService = Depends(get_user_service),\n):\n    return await service.update(id, data)\n\n\n@router.delete(\"/{id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_user(\n    id: UUID,\n    service: UserService = Depends(get_user_service),\n):\n    await service.delete(id)\n```\n\n### core/exceptions.py — Custom Exceptions\n\n```python\n# src/myapp/core/exceptions.py\nfrom fastapi import HTTPException, status\n\n\nclass AppError(Exception):\n    \"\"\"Base application error.\"\"\"\n\n    def __init__(self, message: str, code: str):\n        self.message = message\n        self.code = code\n        super().__init__(message)\n\n\nclass NotFoundError(AppError):\n    def __init__(self, resource: str, id: str):\n        super().__init__(f\"{resource} not found: {id}\", \"NOT_FOUND\")\n\n\nclass ConflictError(AppError):\n    def __init__(self, message: str):\n        super().__init__(message, \"CONFLICT\")\n\n\nclass ValidationError(AppError):\n    def __init__(self, message: str):\n        super().__init__(message, \"VALIDATION_ERROR\")\n\n\n# FastAPI exception handler\ndef app_error_to_http(error: AppError) -> HTTPException:\n    status_map = {\n        \"NOT_FOUND\": status.HTTP_404_NOT_FOUND,\n        \"CONFLICT\": status.HTTP_409_CONFLICT,\n        \"VALIDATION_ERROR\": status.HTTP_400_BAD_REQUEST,\n    }\n    return HTTPException(\n        status_code=status_map.get(error.code, status.HTTP_500_INTERNAL_SERVER_ERROR),\n        detail={\"message\": error.message, \"code\": error.code},\n    )\n```\n\n---\n\n## pyproject.toml\n\n```toml\n[project]\nname = \"myapp\"\nversion = \"0.1.0\"\ndescription = \"My application\"\nrequires-python = \">=3.12\"\ndependencies = [\n    \"fastapi\",\n    \"uvicorn[standard]\",\n    \"pydantic\",\n    \"pydantic-settings\",\n    \"sqlalchemy[asyncio]\",\n    \"asyncpg\",\n    \"httpx\",\n    \"structlog\",\n]\n\n[tool.uv]\ndev-dependencies = [\n    \"pytest\",\n    \"pytest-asyncio\",\n    \"pytest-cov\",\n    \"ruff\",\n    \"mypy\",\n]\n\n[tool.ruff]\nline-length = 100\ntarget-version = \"py312\"\n\n[tool.ruff.lint]\nselect = [\n    \"E\",   # pycodestyle errors\n    \"F\",   # pyflakes\n    \"I\",   # isort\n    \"UP\",  # pyupgrade\n    \"B\",   # flake8-bugbear\n    \"SIM\", # flake8-simplify\n]\n\n[tool.ruff.lint.isort]\nknown-first-party = [\"myapp\"]\n\n[tool.mypy]\nstrict = true\npython_version = \"3.12\"\n\n[tool.pytest.ini_options]\nasyncio_mode = \"auto\"\ntestpaths = [\"tests\"]\n```\n\n---\n\n## Testing\n\n```python\n# tests/conftest.py\nimport pytest\nfrom httpx import ASGITransport, AsyncClient\n\nfrom myapp.main import app\n\n\n@pytest.fixture\nasync def client():\n    async with AsyncClient(\n        transport=ASGITransport(app=app),\n        base_url=\"http://test\",\n    ) as client:\n        yield client\n\n\n# tests/test_user.py\nimport pytest\n\n\n@pytest.mark.asyncio\nasync def test_create_user(client):\n    response = await client.post(\n        \"/api/v1/users\",\n        json={\"email\": \"test@example.com\", \"name\": \"Test User\"},\n    )\n    assert response.status_code == 201\n    data = response.json()\n    assert data[\"email\"] == \"test@example.com\"\n\n\n@pytest.mark.asyncio\nasync def test_get_user_not_found(client):\n    response = await client.get(\"/api/v1/users/00000000-0000-0000-0000-000000000000\")\n    assert response.status_code == 404\n```\n\n---\n\n## Makefile\n\n```makefile\n.PHONY: dev test lint fmt check clean\n\n# Run development server\ndev:\n\tuv run uvicorn myapp.main:app --reload\n\n# Run tests\ntest:\n\tuv run pytest\n\n# Run tests with coverage\ntest-cov:\n\tuv run pytest --cov=myapp --cov-report=html\n\n# Lint code\nlint:\n\tuv run ruff check src tests\n\n# Format code\nfmt:\n\tuv run ruff format src tests\n\tuv run ruff check --fix src tests\n\n# Type check\ntypecheck:\n\tuv run mypy src\n\n# Run all checks\ncheck: fmt lint typecheck test\n\t@echo \"All checks passed!\"\n\n# Clean\nclean:\n\trm -rf .pytest_cache .mypy_cache .ruff_cache htmlcov .coverage\n\tfind . -type d -name __pycache__ -exec rm -rf {} +\n\n# Sync dependencies\nsync:\n\tuv sync\n\n# Upgrade dependencies\nupgrade:\n\tuv lock --upgrade\n\tuv sync\n```\n\n---\n\n## Checklist\n\n```markdown\n## Project Setup\n- [ ] uv initialized with pyproject.toml\n- [ ] .python-version set (3.12+)\n- [ ] src/ layout structure\n- [ ] Ruff configured\n- [ ] mypy strict mode\n\n## Architecture\n- [ ] Pydantic models for validation\n- [ ] Services for business logic\n- [ ] Repositories for data access\n- [ ] Custom exceptions\n- [ ] Dependency injection\n\n## Quality\n- [ ] pytest with pytest-asyncio\n- [ ] Type hints everywhere\n- [ ] Structured logging\n- [ ] Error handling middleware\n\n## CI\n- [ ] ruff check\n- [ ] ruff format --check\n- [ ] mypy\n- [ ] pytest\n```\n\n---\n\n## See Also\n\n- [reference/architecture.md](reference/architecture.md) — Project structure patterns\n- [reference/tech-stack.md](reference/tech-stack.md) — Tool comparisons\n- [reference/patterns.md](reference/patterns.md) — Python design patterns","tags":["python","project","claude","arsenal","majiayu000","agent-skills","ai-agents","ai-coding-assistant","automation","claude-code","code-review","developer-tools"],"capabilities":["skill","source-majiayu000","skill-python-project","topic-agent-skills","topic-ai-agents","topic-ai-coding-assistant","topic-automation","topic-claude","topic-claude-code","topic-code-review","topic-developer-tools","topic-devops","topic-productivity","topic-prompt-engineering","topic-python"],"categories":["claude-arsenal"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/majiayu000/claude-arsenal/python-project","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add majiayu000/claude-arsenal","source_repo":"https://github.com/majiayu000/claude-arsenal","install_from":"skills.sh"}},"qualityScore":"0.464","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 29 github stars · SKILL.md body (12,519 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-01T07:01:15.674Z","embedding":null,"createdAt":"2026-04-18T22:24:22.985Z","updatedAt":"2026-05-01T07:01:15.674Z","lastSeenAt":"2026-05-01T07:01:15.674Z","tsv":"'-3':262 '-8':568 '/api/v1':535 '/api/v1/users':1157 '/api/v1/users/00000000-0000-0000-0000-000000000000':1186 '/health':537 '/users':810 '/uv/install.sh':293 '0':275 '0.1.0':1031 '1':280 '100':626,649,1069 '2':329,623,646 '2.0':164,352 '201':841,1167 '2025':10 '204':888 '3':399 '3.12':307,1038,1104,1321 '400':1006 '4000':230,587 '404':996,1190 '409':1001 '4o':245,260 '500':1016 'access':450,1342 'add':311,314,321,384 'alia':135 'alreadi':738 'also':1370 'alway':366 'api':16,95,196,204,231,234,454,589 'api/routes':776 'apirout':788,808 'app':432,515,524,569,984,1125,1135,1136,1208 'app.get':536 'app.include':531 'apperror':917,938,957,969,989 'appli':330 'applic':483,920,1034 'architectur':7,30,479,1330 'around':119 'asgitransport':1120,1134 'assert':1164,1170,1187 'astral.sh':292 'astral.sh/uv/install.sh':291 'async':26,72,77,79,236,512,538,699,720,744,762,818,843,864,891,1127,1130,1148,1175 'asynccli':1121,1132 'asynccontextmanag':489,511 'asyncio':326,361,1048,1059,1107,1352 'asyncopenai':211,224 'asyncpg':353,1049 'attribut':664 'auto':1109 'await':254,522,707,728,741,754,758,770,773,831,856,879,903,1155,1184 'b':1085 'backward':84,104,144 'bad':115,134,146,156,1007 'base':225,919,1137 'basemodel':610,615,633 'baseset':553,557 'bash':283 'behavior':161,166 'black':54 'bool':574 'bugbear':1088 'build':392 'busi':443,667,1337 'cach':1281,1283,1285 'call':81,202,247 'cd':301 'chain':82 'chang':90,109 'check':158,342,1198,1238,1253,1258,1266,1267,1274,1363,1366 'checklist':1309 'ci':1361 'class':555,613,627,631,650,690,916,936,955,967 'claud':261 'clean':1199,1276,1277 'cli':17 'client':223,355,1129,1141,1143,1153,1182 'client.chat.completions.create':255 'client.get':1185 'client.post':1156 'code':62,108,839,886,927,932,1012,1023,1166,1189,1233,1242 'commit':413 'comparison':1379 'compat':85,105,112,145,387 'complet':238 'config':408,559,662 'config.py':433,544 'configur':1326 'conflict':966,999,1002 'conflicterror':679,736,956 'conftest.py':475 'connect':216 'content':271,890 'contextlib':487 'core':31,465 'core/exceptions.py':906 'cov':1062,1222,1226,1229 'cov-report':1228 'cover':20 'coverag':1219,1287 'creat':13,295,655,722,842,845,1151 'curl':289 'custom':907,1343 'd':1290 'data':18,155,192,449,724,743,750,761,847,858,870,882,1168,1171,1341 'data.email':732 'databas':350,576,577 'datetim':600,602,657,660 'debug':573 'decor':117 'def':122,151,174,190,237,513,539,692,700,721,745,763,819,844,865,892,922,939,958,970,983,1128,1149,1176 'default':642 'delet':86,106,169,764,893 'depend':312,375,460,789,826,851,874,899,1039,1055,1297,1302,1345 'deprec':89,116 'deprecationwarn':130 'deps.py':459 'descript':1032 'design':1383 'detail':1020 'dev':322,1054,1194,1203 'dev-depend':1053 'develop':1201 'direct':91,110,205 'directori':65 'e':1076 'echo':306,1272 'email':616,634,731,737,1159,1172 'emailstr':611,617,635 'encod':566 'engin':510 'engine.dispose':523 'ensur':390 'entir':189 'entri':428 'env':561,563,564 'error':921,979,985,988,1004,1019,1078,1358 'error.code':1014,1024 'error.message':1022 'etc':267 'everyth':44 'everywher':35,1355 'exact':396 'except':908,918,981,1344 'exceptions.py':470 'exec':1293 'exist':727,734,739 'f':948,1079 'fals':575 'fastapi':24,315,349,376,431,482,491,493,516,525,786,912,980,1040 'fetch':385 'field':612,620,641 'file':409,412,562,565 'find':178,1288 'first':1096 'fix':1254 'fixtur':476 'flake8':53,1087,1091 'flake8-bugbear':1086 'flake8-simplify':1090 'fmt':1197,1243,1268 'format':339,1241,1247,1365 'found':951,954,994,998,1181 'framework':348 'function':124,128,133,138,176,182,184 'gemini':265 'gemini-pro':264 'get':701,794,820,827,852,875,900,1178 'good':167,185 'gpt':244,259 'gpt-4o':243,258 'guid':8 'handl':1359 'handler':778,982 'health':540 'hint':34,1354 'html':1231 'htmlcov':1286 'http':354,455,777,987 'httpexcept':914,990,1010 'httpx':319,356,1050,1118 'id':653,703,709,717,748,756,766,772,814,822,833,860,868,881,884,895,905,944,952 'import':120,210,214,488,492,496,500,504,509,552,601,605,609,673,677,682,688,783,787,793,799,805,913,1115,1119,1124,1145 'init':299,424,439,445,451,457,462,468,473,693,923,934,940,947,959,964,971,976 'initi':281,1314 'inject':1346 'instal':284,288,395 'instead':129 'integr':102 'intern':1017 'isort':55,1082 'json':1158 'kept':118 'key':232,235,590 'known':1095 'known-first-parti':1094 'latest':368,380,386 'layer':113,333,456,480 'layout':60,404,1323 'legaci':153 'length':622,625,645,648,1068 'lifespan':514,529,530 'line':1067 'line-length':1066 'lint':338,1196,1232,1234,1269 'litellm':92,97,193,198,218,251,582,583,588 'llm':94,101,195,249 'localhost':229,586 'localhost/myapp':581 'lock':397,411,1305 'log':362,506,519,1357 'logging.py':471 'logic':444,668,1338 'lssf':290 'main':426 'main.py':430,481 'makefil':478,1191,1192 'manag':46,336 'map':992 'markdown':1310 'max':624,647 'messag':268,925,930,935,961,965,973,977,1021 'message.content':276 'middlewar':1360 'min':621,644 'mode':1108,1329 'model':241,256,257,436,438,558,594,596,661,816,836,862,1332 'modern':4 'myapp':300,302,405,423,572,1029,1098,1227 'myapp.api.deps':792 'myapp.api.routes':495 'myapp.config':213,499 'myapp.core.exceptions':676 'myapp.core.logging':503 'myapp.db':508 'myapp.main':1123,1207 'myapp.models.user':681,798 'myapp.repositories.user':687 'myapp.services.user':804 'mypi':39,328,343,1064,1262,1282,1327,1367 'name':140,142,528,570,618,638,1028,1161,1291 'never':369 'new':127,132,139,175,183,296 'none':636,637,640,643,768 'notfounderror':678,714,937 'ok':543 'old':123,141,160,165,181 'openai':209,221 'option':1106 'opus':263 'packag':45,335 'param':154 'paramet':148,188 'parti':1097 'pass':630,1275 'pattern':27,1375,1384 'phoni':1193 'pin':370 'pipelin':19 'point':429 'postgresql':580 'prefix':534,809 'principl':32 'pro':266 'process':152,191 'project':3,6,15,29,282,297,374,1027,1311,1373 'prompt':239,272 'provid':203 'proxi':98,199,219,252 'py':425,427,440,446,452,458,463,469,474 'py312':1073 'pycach':1292 'pycodestyl':1077 'pydant':23,36,317,345,381,434,437,545,550,595,608,1043,1045,1331 'pydantic-set':1044 'pyflak':1080 'pyproject.toml':66,406,1025,1316 'pytest':323,325,358,360,1056,1058,1061,1116,1146,1215,1225,1280,1348,1351,1368 'pytest-asyncio':324,359,1057,1350 'pytest-cov':1060 'pytest.fixture':1126 'pytest.mark.asyncio':1147,1174 'python':2,5,14,28,48,114,206,304,309,416,418,484,547,597,669,779,909,1037,1102,1113,1318,1382 'python-project':1 'python-vers':308,415,1317 'pyupgrad':1084 'qualiti':1347 'quick':278 'rais':713,735 'recommend':334 'reference/architecture.md':1371,1372 'reference/patterns.md':1380,1381 'reference/tech-stack.md':1376,1377 'reload':1209 'remov':186 'renam':137 'replac':52,179 'repo':695,698 'report':1230 'repositori':448,1339 'reproduc':391 'request':1008 'requir':1036 'requirements.txt':71 'requires-python':1035 'resolv':378 'resourc':942,949 'respons':253,815,835,861,1154,1183 'response.choices':274 'response.json':1169 'response.status':1165,1188 'return':131,273,541,718,740,757,830,855,878,1009 'rf':1279,1295 'rm':1278,1294 'role':269 'rout':461 'router':497,532,533,807 'router.delete':883 'router.get':813 'router.patch':859 'router.post':834 'ruff':22,50,327,340,1063,1237,1246,1252,1284,1325,1362,1364 'run':1200,1205,1210,1214,1216,1224,1236,1245,1251,1261,1264 'runtim':38 'sdk':222 'see':1369 'select':1075 'self':694,702,723,747,765,924,941,960,972 'self.code':931 'self.get':755,771 'self.message':929 'self.repo':697 'self.repo.create':742 'self.repo.delete':774 'self.repo.get':708,729 'self.repo.update':759 'server':1018,1202 'servic':442,666,796,824,829,849,854,872,877,897,902,1335 'service.create':857 'service.delete':904 'service.get':832 'service.update':880 'set':215,303,435,501,546,551,556,592,593,1046,1320 'settings.app':527 'settings.litellm':227,233 'settingsconfigdict':554,560 'setup':505,518,1312 'setup.py':69 'sh':294 'share':466 'shutdown':521 'sim':1089 'simplifi':1092 'singl':57,407 'skill' 'skill-python-project' 'source-majiayu000' 'sqlalchemi':318,351,382,1047 'src':59,64,403,422,1239,1248,1255,1263,1322 'src/myapp/api/routes/user.py':780 'src/myapp/config.py':548 'src/myapp/core/exceptions.py':910 'src/myapp/llm.py':207 'src/myapp/main.py':485 'src/myapp/models/user.py':598 'src/myapp/services/user.py':670 'stack':332 'standard':401,1042 'start':279 'startup':517 'static':41 'status':542,790,838,885,915,991,1011 'status.http':840,887,995,1000,1005,1015 'status_map.get':1013 'stay':78 'str':240,242,246,571,579,585,591,619,639,716,926,928,943,945,962,974 'strategi':365 'strict':1100,1328 'structlog':363,1051 'structur':402,1324,1356,1374 'super':933,946,963,975 'sync':394,1296,1298,1300,1308 'tag':811 'target':1071 'target-vers':1070 'tech':331 'templat':372 'test':357,472,1111,1112,1139,1150,1162,1177,1195,1211,1212,1217,1221,1240,1249,1256,1271 'test-cov':1220 'test@example.com':1160,1173 'test_user.py':477 'testpath':1110 'tests/conftest.py':1114 'tests/test_user.py':1144 'titl':526 'toml':373,1026 'tool':58,1378 'tool.mypy':1099 'tool.pytest.ini':1105 'tool.ruff':1065 'tool.ruff.lint':1074 'tool.ruff.lint.isort':1093 'tool.uv':1052 'topic-agent-skills' 'topic-ai-agents' 'topic-ai-coding-assistant' 'topic-automation' 'topic-claude' 'topic-claude-code' 'topic-code-review' 'topic-developer-tools' 'topic-devops' 'topic-productivity' 'topic-prompt-engineering' 'topic-python' 'transport':1133 'true':665,1101 'type':33,341,1257,1289,1353 'typecheck':1259,1270 'underscor':150 'unus':107,147,187 'updat':171,658,746,866 'upgrad':1301,1303,1306 'url':226,228,578,584,1138 'usag':173 'use':11,96,126,197,220,367,400 'user':270,651,683,705,706,712,715,719,726,752,753,760,769,775,795,800,812,817,821,828,837,846,853,863,867,876,894,901,1152,1163,1179 'user.py':441,447,453,464 'userbas':614,629,652 'usercr':628,684,725,801,848 'userrepositori':689,696 'userservic':691,806,825,850,873,898 'userupd':632,685,751,802,871 'utf':567 'util':467 'uuid':604,606,654,672,674,704,749,767,782,784,823,869,896 'uv':21,42,285,298,313,320,337,377,383,393,421,1204,1213,1223,1235,1244,1250,1260,1299,1304,1307,1313 'uv.lock':389,410 'uvicorn':316,1041,1206 'v2':346 'valid':344,978,1003,1334 'validationerror':968 'version':49,157,163,305,310,364,388,398,417,419,1030,1072,1103,1319 'virtualenv':47 'warn':121 'warnings.warn':125 'way':75 'web':347 'yield':520,1142","prices":[{"id":"330b46f9-fa45-4ad6-ab59-33c38ad8650f","listingId":"e3f2646f-d19d-4d88-bc5f-a178dcd0d48c","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"majiayu000","category":"claude-arsenal","install_from":"skills.sh"},"createdAt":"2026-04-18T22:24:22.985Z"}],"sources":[{"listingId":"e3f2646f-d19d-4d88-bc5f-a178dcd0d48c","source":"github","sourceId":"majiayu000/claude-arsenal/python-project","sourceUrl":"https://github.com/majiayu000/claude-arsenal/tree/main/skills/python-project","isPrimary":false,"firstSeenAt":"2026-04-18T22:24:22.985Z","lastSeenAt":"2026-05-01T07:01:15.674Z"}],"details":{"listingId":"e3f2646f-d19d-4d88-bc5f-a178dcd0d48c","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"majiayu000","slug":"python-project","github":{"repo":"majiayu000/claude-arsenal","stars":29,"topics":["agent-skills","ai-agents","ai-coding-assistant","automation","claude","claude-code","code-review","developer-tools","devops","productivity","prompt-engineering","python","software-development","typescript","workflows"],"license":"mit","html_url":"https://github.com/majiayu000/claude-arsenal","pushed_at":"2026-04-29T04:12:22Z","description":"52 production-ready Claude Code skills and 7 specialized agents for software development, DevOps, product workflows, and automation.","skill_md_sha":"eafc914b6aa1bbb7f9927a0d41bc152347bc5c21","skill_md_path":"skills/python-project/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/majiayu000/claude-arsenal/tree/main/skills/python-project"},"layout":"multi","source":"github","category":"claude-arsenal","frontmatter":{"name":"python-project","description":"Modern Python project architecture guide for 2025. Use when creating Python projects (APIs, CLI, data pipelines). Covers uv, Ruff, Pydantic, FastAPI, and async patterns."},"skills_sh_url":"https://skills.sh/majiayu000/claude-arsenal/python-project"},"updatedAt":"2026-05-01T07:01:15.674Z"}}