@hivehub/rulebook 1.2.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/LICENSE +191 -0
- package/README.md +539 -0
- package/dist/agents/claude-code.d.ts +69 -0
- package/dist/agents/claude-code.d.ts.map +1 -0
- package/dist/agents/claude-code.js +180 -0
- package/dist/agents/claude-code.js.map +1 -0
- package/dist/agents/cursor-agent.d.ts +184 -0
- package/dist/agents/cursor-agent.d.ts.map +1 -0
- package/dist/agents/cursor-agent.js +299 -0
- package/dist/agents/cursor-agent.js.map +1 -0
- package/dist/agents/gemini-cli.d.ts +69 -0
- package/dist/agents/gemini-cli.d.ts.map +1 -0
- package/dist/agents/gemini-cli.js +180 -0
- package/dist/agents/gemini-cli.js.map +1 -0
- package/dist/cli/commands.d.ts +57 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +1370 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/docs-prompts.d.ts +3 -0
- package/dist/cli/docs-prompts.d.ts.map +1 -0
- package/dist/cli/docs-prompts.js +45 -0
- package/dist/cli/docs-prompts.js.map +1 -0
- package/dist/cli/prompts.d.ts +6 -0
- package/dist/cli/prompts.d.ts.map +1 -0
- package/dist/cli/prompts.js +376 -0
- package/dist/cli/prompts.js.map +1 -0
- package/dist/core/agent-manager.d.ts +89 -0
- package/dist/core/agent-manager.d.ts.map +1 -0
- package/dist/core/agent-manager.js +546 -0
- package/dist/core/agent-manager.js.map +1 -0
- package/dist/core/auto-fixer.d.ts +14 -0
- package/dist/core/auto-fixer.d.ts.map +1 -0
- package/dist/core/auto-fixer.js +207 -0
- package/dist/core/auto-fixer.js.map +1 -0
- package/dist/core/changelog-generator.d.ts +44 -0
- package/dist/core/changelog-generator.d.ts.map +1 -0
- package/dist/core/changelog-generator.js +222 -0
- package/dist/core/changelog-generator.js.map +1 -0
- package/dist/core/cli-bridge.d.ts +113 -0
- package/dist/core/cli-bridge.d.ts.map +1 -0
- package/dist/core/cli-bridge.js +1094 -0
- package/dist/core/cli-bridge.js.map +1 -0
- package/dist/core/config-manager.d.ts +65 -0
- package/dist/core/config-manager.d.ts.map +1 -0
- package/dist/core/config-manager.js +266 -0
- package/dist/core/config-manager.js.map +1 -0
- package/dist/core/coverage-checker.d.ts +14 -0
- package/dist/core/coverage-checker.d.ts.map +1 -0
- package/dist/core/coverage-checker.js +176 -0
- package/dist/core/coverage-checker.js.map +1 -0
- package/dist/core/custom-templates.d.ts +27 -0
- package/dist/core/custom-templates.d.ts.map +1 -0
- package/dist/core/custom-templates.js +122 -0
- package/dist/core/custom-templates.js.map +1 -0
- package/dist/core/dependency-checker.d.ts +21 -0
- package/dist/core/dependency-checker.d.ts.map +1 -0
- package/dist/core/dependency-checker.js +247 -0
- package/dist/core/dependency-checker.js.map +1 -0
- package/dist/core/detector.d.ts +3 -0
- package/dist/core/detector.d.ts.map +1 -0
- package/dist/core/detector.js +1443 -0
- package/dist/core/detector.js.map +1 -0
- package/dist/core/docs-generator.d.ts +9 -0
- package/dist/core/docs-generator.d.ts.map +1 -0
- package/dist/core/docs-generator.js +531 -0
- package/dist/core/docs-generator.js.map +1 -0
- package/dist/core/generator.d.ts +16 -0
- package/dist/core/generator.d.ts.map +1 -0
- package/dist/core/generator.js +561 -0
- package/dist/core/generator.js.map +1 -0
- package/dist/core/gitignore-generator.d.ts +13 -0
- package/dist/core/gitignore-generator.d.ts.map +1 -0
- package/dist/core/gitignore-generator.js +307 -0
- package/dist/core/gitignore-generator.js.map +1 -0
- package/dist/core/health-scorer.d.ts +22 -0
- package/dist/core/health-scorer.d.ts.map +1 -0
- package/dist/core/health-scorer.js +395 -0
- package/dist/core/health-scorer.js.map +1 -0
- package/dist/core/logger.d.ts +116 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +289 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/merger.d.ts +6 -0
- package/dist/core/merger.d.ts.map +1 -0
- package/dist/core/merger.js +131 -0
- package/dist/core/merger.js.map +1 -0
- package/dist/core/migrator.d.ts +19 -0
- package/dist/core/migrator.d.ts.map +1 -0
- package/dist/core/migrator.js +102 -0
- package/dist/core/migrator.js.map +1 -0
- package/dist/core/minimal-scaffolder.d.ts +8 -0
- package/dist/core/minimal-scaffolder.d.ts.map +1 -0
- package/dist/core/minimal-scaffolder.js +51 -0
- package/dist/core/minimal-scaffolder.js.map +1 -0
- package/dist/core/modern-console-new.d.ts +81 -0
- package/dist/core/modern-console-new.d.ts.map +1 -0
- package/dist/core/modern-console-new.js +340 -0
- package/dist/core/modern-console-new.js.map +1 -0
- package/dist/core/modern-console.d.ts +99 -0
- package/dist/core/modern-console.d.ts.map +1 -0
- package/dist/core/modern-console.js +568 -0
- package/dist/core/modern-console.js.map +1 -0
- package/dist/core/openspec-manager.d.ts +133 -0
- package/dist/core/openspec-manager.d.ts.map +1 -0
- package/dist/core/openspec-manager.js +605 -0
- package/dist/core/openspec-manager.js.map +1 -0
- package/dist/core/openspec-migrator.d.ts +27 -0
- package/dist/core/openspec-migrator.d.ts.map +1 -0
- package/dist/core/openspec-migrator.js +255 -0
- package/dist/core/openspec-migrator.js.map +1 -0
- package/dist/core/task-manager.d.ts +65 -0
- package/dist/core/task-manager.d.ts.map +1 -0
- package/dist/core/task-manager.js +318 -0
- package/dist/core/task-manager.js.map +1 -0
- package/dist/core/test-task-manager.d.ts +49 -0
- package/dist/core/test-task-manager.d.ts.map +1 -0
- package/dist/core/test-task-manager.js +121 -0
- package/dist/core/test-task-manager.js.map +1 -0
- package/dist/core/validator.d.ts +21 -0
- package/dist/core/validator.d.ts.map +1 -0
- package/dist/core/validator.js +177 -0
- package/dist/core/validator.js.map +1 -0
- package/dist/core/version-bumper.d.ts +19 -0
- package/dist/core/version-bumper.d.ts.map +1 -0
- package/dist/core/version-bumper.js +180 -0
- package/dist/core/version-bumper.js.map +1 -0
- package/dist/core/watcher.d.ts +9 -0
- package/dist/core/watcher.d.ts.map +1 -0
- package/dist/core/watcher.js +22 -0
- package/dist/core/watcher.js.map +1 -0
- package/dist/core/workflow-generator.d.ts +10 -0
- package/dist/core/workflow-generator.d.ts.map +1 -0
- package/dist/core/workflow-generator.js +279 -0
- package/dist/core/workflow-generator.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +159 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/handlers/archive-task.d.ts +17 -0
- package/dist/mcp/handlers/archive-task.d.ts.map +1 -0
- package/dist/mcp/handlers/archive-task.js +36 -0
- package/dist/mcp/handlers/archive-task.js.map +1 -0
- package/dist/mcp/handlers/create-task.d.ts +17 -0
- package/dist/mcp/handlers/create-task.d.ts.map +1 -0
- package/dist/mcp/handlers/create-task.js +56 -0
- package/dist/mcp/handlers/create-task.js.map +1 -0
- package/dist/mcp/handlers/list-tasks.d.ts +22 -0
- package/dist/mcp/handlers/list-tasks.d.ts.map +1 -0
- package/dist/mcp/handlers/list-tasks.js +42 -0
- package/dist/mcp/handlers/list-tasks.js.map +1 -0
- package/dist/mcp/handlers/show-task.d.ts +25 -0
- package/dist/mcp/handlers/show-task.d.ts.map +1 -0
- package/dist/mcp/handlers/show-task.js +43 -0
- package/dist/mcp/handlers/show-task.js.map +1 -0
- package/dist/mcp/handlers/update-task.d.ts +17 -0
- package/dist/mcp/handlers/update-task.d.ts.map +1 -0
- package/dist/mcp/handlers/update-task.js +35 -0
- package/dist/mcp/handlers/update-task.js.map +1 -0
- package/dist/mcp/handlers/validate-task.d.ts +15 -0
- package/dist/mcp/handlers/validate-task.d.ts.map +1 -0
- package/dist/mcp/handlers/validate-task.js +27 -0
- package/dist/mcp/handlers/validate-task.js.map +1 -0
- package/dist/mcp/rulebook-config.d.ts +22 -0
- package/dist/mcp/rulebook-config.d.ts.map +1 -0
- package/dist/mcp/rulebook-config.js +65 -0
- package/dist/mcp/rulebook-config.js.map +1 -0
- package/dist/mcp/rulebook-server.d.ts +4 -0
- package/dist/mcp/rulebook-server.d.ts.map +1 -0
- package/dist/mcp/rulebook-server.js +246 -0
- package/dist/mcp/rulebook-server.js.map +1 -0
- package/dist/types.d.ts +190 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/file-system.d.ts +9 -0
- package/dist/utils/file-system.d.ts.map +1 -0
- package/dist/utils/file-system.js +51 -0
- package/dist/utils/file-system.js.map +1 -0
- package/dist/utils/git-hooks.d.ts +8 -0
- package/dist/utils/git-hooks.d.ts.map +1 -0
- package/dist/utils/git-hooks.js +440 -0
- package/dist/utils/git-hooks.js.map +1 -0
- package/dist/utils/rulesignore.d.ts +9 -0
- package/dist/utils/rulesignore.d.ts.map +1 -0
- package/dist/utils/rulesignore.js +42 -0
- package/dist/utils/rulesignore.js.map +1 -0
- package/package.json +106 -0
- package/templates/cli/AIDER.md +49 -0
- package/templates/cli/AMAZON_Q.md +25 -0
- package/templates/cli/AUGGIE.md +32 -0
- package/templates/cli/CLAUDE.md +32 -0
- package/templates/cli/CLAUDE_CODE.md +35 -0
- package/templates/cli/CLINE.md +32 -0
- package/templates/cli/CODEBUDDY.md +20 -0
- package/templates/cli/CODEIUM.md +20 -0
- package/templates/cli/CODEX.md +21 -0
- package/templates/cli/CONTINUE.md +34 -0
- package/templates/cli/CURSOR_CLI.md +28 -0
- package/templates/cli/FACTORY.md +18 -0
- package/templates/cli/GEMINI.md +35 -0
- package/templates/cli/KILOCODE.md +18 -0
- package/templates/cli/OPENCODE.md +18 -0
- package/templates/cli/_GENERIC_TEMPLATE.md +29 -0
- package/templates/commands/rulebook-task-apply.md +67 -0
- package/templates/commands/rulebook-task-archive.md +70 -0
- package/templates/commands/rulebook-task-create.md +93 -0
- package/templates/commands/rulebook-task-list.md +42 -0
- package/templates/commands/rulebook-task-show.md +52 -0
- package/templates/commands/rulebook-task-validate.md +53 -0
- package/templates/core/AGENT_AUTOMATION.md +184 -0
- package/templates/core/DAG.md +304 -0
- package/templates/core/DOCUMENTATION_RULES.md +37 -0
- package/templates/core/QUALITY_ENFORCEMENT.md +68 -0
- package/templates/core/RULEBOOK.md +1874 -0
- package/templates/frameworks/ANGULAR.md +36 -0
- package/templates/frameworks/DJANGO.md +83 -0
- package/templates/frameworks/ELECTRON.md +147 -0
- package/templates/frameworks/FLASK.md +38 -0
- package/templates/frameworks/FLUTTER.md +55 -0
- package/templates/frameworks/JQUERY.md +32 -0
- package/templates/frameworks/LARAVEL.md +38 -0
- package/templates/frameworks/NESTJS.md +43 -0
- package/templates/frameworks/NEXTJS.md +127 -0
- package/templates/frameworks/NUXT.md +40 -0
- package/templates/frameworks/RAILS.md +66 -0
- package/templates/frameworks/REACT.md +38 -0
- package/templates/frameworks/REACT_NATIVE.md +47 -0
- package/templates/frameworks/SPRING.md +39 -0
- package/templates/frameworks/SYMFONY.md +36 -0
- package/templates/frameworks/VUE.md +36 -0
- package/templates/frameworks/ZEND.md +35 -0
- package/templates/git/CI_CD_PATTERNS.md +661 -0
- package/templates/git/GITHUB_ACTIONS.md +728 -0
- package/templates/git/GITLAB_CI.md +730 -0
- package/templates/git/GIT_WORKFLOW.md +1157 -0
- package/templates/git/SECRETS_MANAGEMENT.md +585 -0
- package/templates/hooks/COMMIT_MSG.md +530 -0
- package/templates/hooks/POST_CHECKOUT.md +546 -0
- package/templates/hooks/PREPARE_COMMIT_MSG.md +619 -0
- package/templates/hooks/PRE_COMMIT.md +414 -0
- package/templates/hooks/PRE_PUSH.md +601 -0
- package/templates/hooks/csharp-pre-commit.sh +23 -0
- package/templates/hooks/csharp-pre-push.sh +23 -0
- package/templates/hooks/dart-pre-commit.sh +30 -0
- package/templates/hooks/dart-pre-push.sh +25 -0
- package/templates/hooks/elixir-pre-commit.sh +32 -0
- package/templates/hooks/elixir-pre-push.sh +31 -0
- package/templates/hooks/erlang-pre-commit.sh +30 -0
- package/templates/hooks/erlang-pre-push.sh +37 -0
- package/templates/hooks/go-pre-commit.sh +40 -0
- package/templates/hooks/go-pre-push.sh +31 -0
- package/templates/hooks/haskell-pre-commit.sh +41 -0
- package/templates/hooks/haskell-pre-push.sh +37 -0
- package/templates/hooks/java-pre-commit.sh +34 -0
- package/templates/hooks/java-pre-push.sh +24 -0
- package/templates/hooks/kotlin-pre-commit.sh +32 -0
- package/templates/hooks/kotlin-pre-push.sh +16 -0
- package/templates/hooks/php-pre-commit.sh +36 -0
- package/templates/hooks/php-pre-push.sh +26 -0
- package/templates/hooks/python-pre-commit.sh +51 -0
- package/templates/hooks/python-pre-push.sh +25 -0
- package/templates/hooks/ruby-pre-commit.sh +33 -0
- package/templates/hooks/ruby-pre-push.sh +32 -0
- package/templates/hooks/rust-pre-commit.sh +30 -0
- package/templates/hooks/rust-pre-push.sh +30 -0
- package/templates/hooks/scala-pre-commit.sh +32 -0
- package/templates/hooks/scala-pre-push.sh +24 -0
- package/templates/hooks/swift-pre-commit.sh +25 -0
- package/templates/hooks/swift-pre-push.sh +23 -0
- package/templates/hooks/typescript-pre-commit.sh +37 -0
- package/templates/hooks/typescript-pre-push.sh +36 -0
- package/templates/ides/COPILOT.md +37 -0
- package/templates/ides/CURSOR.md +43 -0
- package/templates/ides/JETBRAINS_AI.md +35 -0
- package/templates/ides/REPLIT.md +36 -0
- package/templates/ides/TABNINE.md +29 -0
- package/templates/ides/VSCODE.md +40 -0
- package/templates/ides/WINDSURF.md +36 -0
- package/templates/ides/ZED.md +32 -0
- package/templates/languages/ADA.md +58 -0
- package/templates/languages/C.md +333 -0
- package/templates/languages/CPP.md +743 -0
- package/templates/languages/CSHARP.md +417 -0
- package/templates/languages/DART.md +332 -0
- package/templates/languages/ELIXIR.md +454 -0
- package/templates/languages/ERLANG.md +361 -0
- package/templates/languages/GO.md +645 -0
- package/templates/languages/HASKELL.md +177 -0
- package/templates/languages/JAVA.md +607 -0
- package/templates/languages/JAVASCRIPT.md +631 -0
- package/templates/languages/JULIA.md +97 -0
- package/templates/languages/KOTLIN.md +511 -0
- package/templates/languages/LISP.md +100 -0
- package/templates/languages/LUA.md +74 -0
- package/templates/languages/OBJECTIVEC.md +90 -0
- package/templates/languages/PHP.md +416 -0
- package/templates/languages/PYTHON.md +682 -0
- package/templates/languages/R.md +350 -0
- package/templates/languages/RUBY.md +421 -0
- package/templates/languages/RUST.md +477 -0
- package/templates/languages/SAS.md +73 -0
- package/templates/languages/SCALA.md +348 -0
- package/templates/languages/SOLIDITY.md +580 -0
- package/templates/languages/SQL.md +137 -0
- package/templates/languages/SWIFT.md +466 -0
- package/templates/languages/TYPESCRIPT.md +591 -0
- package/templates/languages/ZIG.md +265 -0
- package/templates/modules/ATLASSIAN.md +255 -0
- package/templates/modules/CONTEXT7.md +54 -0
- package/templates/modules/FIGMA.md +267 -0
- package/templates/modules/GITHUB_MCP.md +64 -0
- package/templates/modules/GRAFANA.md +328 -0
- package/templates/modules/NOTION.md +247 -0
- package/templates/modules/PLAYWRIGHT.md +90 -0
- package/templates/modules/RULEBOOK_MCP.md +156 -0
- package/templates/modules/SERENA.md +337 -0
- package/templates/modules/SUPABASE.md +223 -0
- package/templates/modules/SYNAP.md +69 -0
- package/templates/modules/VECTORIZER.md +63 -0
- package/templates/services/AZURE_BLOB.md +184 -0
- package/templates/services/CASSANDRA.md +239 -0
- package/templates/services/DYNAMODB.md +308 -0
- package/templates/services/ELASTICSEARCH.md +347 -0
- package/templates/services/GCS.md +178 -0
- package/templates/services/INFLUXDB.md +265 -0
- package/templates/services/KAFKA.md +341 -0
- package/templates/services/MARIADB.md +183 -0
- package/templates/services/MEMCACHED.md +242 -0
- package/templates/services/MINIO.md +201 -0
- package/templates/services/MONGODB.md +268 -0
- package/templates/services/MYSQL.md +358 -0
- package/templates/services/NEO4J.md +247 -0
- package/templates/services/ORACLE.md +290 -0
- package/templates/services/POSTGRESQL.md +326 -0
- package/templates/services/RABBITMQ.md +286 -0
- package/templates/services/REDIS.md +292 -0
- package/templates/services/S3.md +298 -0
- package/templates/services/SQLITE.md +294 -0
- package/templates/services/SQLSERVER.md +294 -0
- package/templates/workflows/codespell.yml +31 -0
- package/templates/workflows/cpp-lint.yml +47 -0
- package/templates/workflows/cpp-publish.yml +119 -0
- package/templates/workflows/cpp-test.yml +77 -0
- package/templates/workflows/dotnet-lint.yml +29 -0
- package/templates/workflows/dotnet-publish.yml +40 -0
- package/templates/workflows/dotnet-test.yml +41 -0
- package/templates/workflows/elixir-lint.yml +45 -0
- package/templates/workflows/elixir-publish.yml +49 -0
- package/templates/workflows/elixir-test.yml +54 -0
- package/templates/workflows/erlang-lint.yml +47 -0
- package/templates/workflows/erlang-test.yml +62 -0
- package/templates/workflows/go-lint.yml +39 -0
- package/templates/workflows/go-publish.yml +95 -0
- package/templates/workflows/go-test.yml +59 -0
- package/templates/workflows/java-lint.yml +60 -0
- package/templates/workflows/java-publish.yml +120 -0
- package/templates/workflows/java-test.yml +85 -0
- package/templates/workflows/kotlin-lint.yml +34 -0
- package/templates/workflows/kotlin-publish.yml +56 -0
- package/templates/workflows/kotlin-test.yml +48 -0
- package/templates/workflows/php-lint.yml +39 -0
- package/templates/workflows/php-publish.yml +50 -0
- package/templates/workflows/php-test.yml +54 -0
- package/templates/workflows/python-lint.yml +47 -0
- package/templates/workflows/python-publish.yml +91 -0
- package/templates/workflows/python-test.yml +59 -0
- package/templates/workflows/rust-lint.yml +54 -0
- package/templates/workflows/rust-publish.yml +66 -0
- package/templates/workflows/rust-test.yml +75 -0
- package/templates/workflows/solidity-lint.yml +41 -0
- package/templates/workflows/solidity-test.yml +47 -0
- package/templates/workflows/swift-lint.yml +32 -0
- package/templates/workflows/swift-publish.yml +58 -0
- package/templates/workflows/swift-test.yml +44 -0
- package/templates/workflows/typescript-lint.yml +61 -0
- package/templates/workflows/typescript-publish.yml +60 -0
- package/templates/workflows/typescript-test.yml +73 -0
- package/templates/workflows/zig-lint.yml +27 -0
- package/templates/workflows/zig-test.yml +40 -0
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
<!-- PYTHON:START -->
|
|
2
|
+
# Python Project Rules
|
|
3
|
+
|
|
4
|
+
## Agent Automation Commands
|
|
5
|
+
|
|
6
|
+
**CRITICAL**: Execute these commands after EVERY implementation (see AGENT_AUTOMATION module for full workflow).
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
# Complete quality check sequence:
|
|
10
|
+
ruff format --check . # Format check
|
|
11
|
+
ruff check . # Linting
|
|
12
|
+
mypy . # Type checking
|
|
13
|
+
pytest # All tests (100% pass required)
|
|
14
|
+
pytest --cov # Coverage check (95%+ required)
|
|
15
|
+
|
|
16
|
+
# Security audit:
|
|
17
|
+
pip-audit # Vulnerability scan
|
|
18
|
+
pip list --outdated # Check outdated deps
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Python Version
|
|
22
|
+
|
|
23
|
+
**CRITICAL**: Use Python 3.11+ for modern features and performance.
|
|
24
|
+
|
|
25
|
+
- **Minimum Version**: Python 3.11+
|
|
26
|
+
- **Recommended**: Python 3.12+
|
|
27
|
+
- **Type Hints**: Required for all public APIs
|
|
28
|
+
|
|
29
|
+
### Formatting
|
|
30
|
+
|
|
31
|
+
- Use `ruff format` (fast, modern) or `black` (traditional)
|
|
32
|
+
- Line length: 100 characters (configurable)
|
|
33
|
+
- Consistent formatting across entire project
|
|
34
|
+
- Format before committing
|
|
35
|
+
|
|
36
|
+
Configuration in `pyproject.toml`:
|
|
37
|
+
```toml
|
|
38
|
+
[tool.ruff]
|
|
39
|
+
line-length = 100
|
|
40
|
+
target-version = "py311"
|
|
41
|
+
|
|
42
|
+
[tool.ruff.format]
|
|
43
|
+
quote-style = "double"
|
|
44
|
+
indent-style = "space"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Linting
|
|
48
|
+
|
|
49
|
+
- Use `ruff check` (fast, comprehensive) or `ruff` + `flake8`
|
|
50
|
+
- Fix all linting errors before committing
|
|
51
|
+
- Document any disabled rules with justification
|
|
52
|
+
|
|
53
|
+
Configuration in `pyproject.toml`:
|
|
54
|
+
```toml
|
|
55
|
+
[tool.ruff.lint]
|
|
56
|
+
select = ["E", "F", "I", "N", "W", "UP", "B", "A", "C4", "SIM"]
|
|
57
|
+
ignore = ["E501"] # Line too long (handled by formatter)
|
|
58
|
+
|
|
59
|
+
[tool.ruff.lint.per-file-ignores]
|
|
60
|
+
"tests/*" = ["S101"] # Allow assert in tests
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Type Checking
|
|
64
|
+
|
|
65
|
+
- Use `mypy` for static type checking
|
|
66
|
+
- All public APIs must have type hints
|
|
67
|
+
- Use `typing` module for complex types
|
|
68
|
+
- Gradual typing allowed for legacy code
|
|
69
|
+
|
|
70
|
+
Configuration in `pyproject.toml`:
|
|
71
|
+
```toml
|
|
72
|
+
[tool.mypy]
|
|
73
|
+
python_version = "3.11"
|
|
74
|
+
strict = true
|
|
75
|
+
warn_return_any = true
|
|
76
|
+
warn_unused_configs = true
|
|
77
|
+
disallow_untyped_defs = true
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Example:
|
|
81
|
+
```python
|
|
82
|
+
from typing import Optional, List, Dict, Any
|
|
83
|
+
|
|
84
|
+
def process_data(
|
|
85
|
+
input_data: str,
|
|
86
|
+
options: Optional[Dict[str, Any]] = None
|
|
87
|
+
) -> List[str]:
|
|
88
|
+
"""Process input data and return results."""
|
|
89
|
+
# Implementation
|
|
90
|
+
return []
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Testing
|
|
94
|
+
|
|
95
|
+
- **Framework**: pytest
|
|
96
|
+
- **Location**: `/tests` directory
|
|
97
|
+
- **Coverage**: Must meet project threshold (default 95%)
|
|
98
|
+
- **Fixtures**: Use pytest fixtures for setup/teardown
|
|
99
|
+
- **Parametrize**: Use `@pytest.mark.parametrize` for multiple test cases
|
|
100
|
+
|
|
101
|
+
Example test structure:
|
|
102
|
+
```python
|
|
103
|
+
import pytest
|
|
104
|
+
from mymodule import process_data
|
|
105
|
+
|
|
106
|
+
@pytest.fixture
|
|
107
|
+
def sample_data():
|
|
108
|
+
"""Provide sample data for tests."""
|
|
109
|
+
return "test input"
|
|
110
|
+
|
|
111
|
+
def test_process_data_valid_input(sample_data):
|
|
112
|
+
"""Test process_data with valid input."""
|
|
113
|
+
result = process_data(sample_data)
|
|
114
|
+
assert result == ["expected"]
|
|
115
|
+
|
|
116
|
+
@pytest.mark.parametrize("input_val,expected", [
|
|
117
|
+
("hello", ["HELLO"]),
|
|
118
|
+
("world", ["WORLD"]),
|
|
119
|
+
])
|
|
120
|
+
def test_process_data_parametrized(input_val, expected):
|
|
121
|
+
"""Test multiple input cases."""
|
|
122
|
+
result = process_data(input_val)
|
|
123
|
+
assert result == expected
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Test Categories: S2S and Slow Tests
|
|
127
|
+
|
|
128
|
+
**CRITICAL**: Tests must be categorized based on execution time and dependencies.
|
|
129
|
+
|
|
130
|
+
#### Test Time Limits
|
|
131
|
+
|
|
132
|
+
- **Fast Tests**: Must complete in ≤ 10-20 seconds
|
|
133
|
+
- **Slow Tests**: Any test taking > 10-20 seconds must be marked as slow
|
|
134
|
+
- **S2S Tests**: Tests requiring active server/database must be isolated and run on-demand
|
|
135
|
+
|
|
136
|
+
#### S2S (Server-to-Server) Tests
|
|
137
|
+
|
|
138
|
+
**Tests that require active servers, databases, or external services must be isolated using pytest markers.**
|
|
139
|
+
|
|
140
|
+
**Implementation**:
|
|
141
|
+
|
|
142
|
+
1. **Mark S2S tests with pytest markers**:
|
|
143
|
+
```python
|
|
144
|
+
import pytest
|
|
145
|
+
import os
|
|
146
|
+
|
|
147
|
+
# Regular fast test (always runs)
|
|
148
|
+
def test_local_computation():
|
|
149
|
+
"""Fast test, no external dependencies."""
|
|
150
|
+
result = compute_locally("input")
|
|
151
|
+
assert result == "expected"
|
|
152
|
+
|
|
153
|
+
# S2S test (only runs with -m s2s)
|
|
154
|
+
@pytest.mark.s2s
|
|
155
|
+
def test_database_connection():
|
|
156
|
+
"""Requires active database server."""
|
|
157
|
+
db = connect_to_database()
|
|
158
|
+
# ... test implementation
|
|
159
|
+
|
|
160
|
+
@pytest.mark.s2s
|
|
161
|
+
def test_api_integration():
|
|
162
|
+
"""Requires active API server."""
|
|
163
|
+
client = create_api_client()
|
|
164
|
+
# ... test implementation
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
2. **Configure `pytest.ini` or `pyproject.toml`**:
|
|
168
|
+
```ini
|
|
169
|
+
# pytest.ini
|
|
170
|
+
[pytest]
|
|
171
|
+
markers =
|
|
172
|
+
s2s: Server-to-server tests requiring active services
|
|
173
|
+
slow: Slow tests taking > 20 seconds
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Or in `pyproject.toml`:
|
|
177
|
+
```toml
|
|
178
|
+
[tool.pytest.ini_options]
|
|
179
|
+
markers = [
|
|
180
|
+
"s2s: Server-to-server tests requiring active services",
|
|
181
|
+
"slow: Slow tests taking > 20 seconds",
|
|
182
|
+
]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
3. **Run tests**:
|
|
186
|
+
```bash
|
|
187
|
+
# Regular tests (excludes S2S)
|
|
188
|
+
pytest
|
|
189
|
+
|
|
190
|
+
# Include S2S tests (requires active servers)
|
|
191
|
+
pytest -m s2s
|
|
192
|
+
|
|
193
|
+
# Run all tests including S2S
|
|
194
|
+
pytest -m "not slow" # Fast + S2S, excludes slow
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### Slow Tests
|
|
198
|
+
|
|
199
|
+
**Tests that take > 10-20 seconds must be marked and run separately.**
|
|
200
|
+
|
|
201
|
+
**Implementation**:
|
|
202
|
+
|
|
203
|
+
1. **Mark slow tests with pytest markers**:
|
|
204
|
+
```python
|
|
205
|
+
import pytest
|
|
206
|
+
|
|
207
|
+
# Fast test (always runs)
|
|
208
|
+
def test_quick_operation():
|
|
209
|
+
"""Completes in < 1 second."""
|
|
210
|
+
result = quick_compute("input")
|
|
211
|
+
assert result == "expected"
|
|
212
|
+
|
|
213
|
+
# Slow test (only runs with -m slow)
|
|
214
|
+
@pytest.mark.slow
|
|
215
|
+
def test_heavy_computation():
|
|
216
|
+
"""Takes 30+ seconds."""
|
|
217
|
+
# Heavy processing, large dataset, etc.
|
|
218
|
+
result = process_large_dataset()
|
|
219
|
+
assert result is not None
|
|
220
|
+
|
|
221
|
+
@pytest.mark.slow
|
|
222
|
+
def test_large_file_processing():
|
|
223
|
+
"""Processes large files, takes > 20 seconds."""
|
|
224
|
+
result = process_file("large_file.dat")
|
|
225
|
+
assert result.success
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
2. **Run tests**:
|
|
229
|
+
```bash
|
|
230
|
+
# Regular tests (excludes slow and S2S)
|
|
231
|
+
pytest -m "not slow and not s2s"
|
|
232
|
+
|
|
233
|
+
# Include slow tests
|
|
234
|
+
pytest -m slow
|
|
235
|
+
|
|
236
|
+
# Run all tests
|
|
237
|
+
pytest -m "" # Empty marker means all tests
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
3. **Add pytest configuration for timeouts**:
|
|
241
|
+
```python
|
|
242
|
+
# conftest.py
|
|
243
|
+
import pytest
|
|
244
|
+
|
|
245
|
+
@pytest.fixture(autouse=True)
|
|
246
|
+
def configure_timeouts(request):
|
|
247
|
+
"""Configure timeouts based on test markers."""
|
|
248
|
+
if 'slow' in request.keywords:
|
|
249
|
+
request.node.add_marker(pytest.mark.timeout(300)) # 5 minutes
|
|
250
|
+
elif 's2s' in request.keywords:
|
|
251
|
+
request.node.add_marker(pytest.mark.timeout(60)) # 1 minute
|
|
252
|
+
else:
|
|
253
|
+
request.node.add_marker(pytest.mark.timeout(20)) # 20 seconds
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
4. **Add scripts in `pyproject.toml` or `setup.py`**:
|
|
257
|
+
```toml
|
|
258
|
+
[tool.poetry.scripts]
|
|
259
|
+
test = "pytest -m 'not slow and not s2s'"
|
|
260
|
+
test-s2s = "pytest -m s2s"
|
|
261
|
+
test-slow = "pytest -m slow"
|
|
262
|
+
test-all = "pytest"
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
#### Best Practices
|
|
266
|
+
|
|
267
|
+
- ✅ **Always run fast tests** in CI/CD by default
|
|
268
|
+
- ✅ **Isolate S2S tests** - never run them in standard test suite
|
|
269
|
+
- ✅ **Mark slow tests** - prevent CI/CD timeouts
|
|
270
|
+
- ✅ **Document requirements** - specify which servers/services are needed for S2S tests
|
|
271
|
+
- ✅ **Use timeouts** - Set appropriate timeouts: `@pytest.mark.timeout(60)`
|
|
272
|
+
- ✅ **Use pytest markers** - `@pytest.mark.s2s` and `@pytest.mark.slow`
|
|
273
|
+
- ✅ **Skip conditionally** - `@pytest.mark.skipif(not os.getenv('RUN_S2S_TESTS'), reason='S2S tests disabled')`
|
|
274
|
+
- ❌ **Never mix** fast and slow/S2S tests in same test run
|
|
275
|
+
- ❌ **Never require** external services for standard test suite
|
|
276
|
+
- ❌ **Never exceed** 10-20 seconds for regular tests
|
|
277
|
+
|
|
278
|
+
## Dependency Management
|
|
279
|
+
|
|
280
|
+
**CRITICAL**: Use modern dependency management tools.
|
|
281
|
+
|
|
282
|
+
### Recommended: Poetry
|
|
283
|
+
|
|
284
|
+
```toml
|
|
285
|
+
[tool.poetry]
|
|
286
|
+
name = "myproject"
|
|
287
|
+
version = "0.1.0"
|
|
288
|
+
description = ""
|
|
289
|
+
authors = ["Your Name <you@example.com>"]
|
|
290
|
+
|
|
291
|
+
[tool.poetry.dependencies]
|
|
292
|
+
python = "^3.11"
|
|
293
|
+
requests = "^2.31.0"
|
|
294
|
+
|
|
295
|
+
[tool.poetry.group.dev.dependencies]
|
|
296
|
+
pytest = "^7.4.0"
|
|
297
|
+
mypy = "^1.5.0"
|
|
298
|
+
ruff = "^0.1.0"
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Commands:
|
|
302
|
+
```bash
|
|
303
|
+
poetry install # Install dependencies
|
|
304
|
+
poetry add requests # Add dependency
|
|
305
|
+
poetry add --group dev pytest # Add dev dependency
|
|
306
|
+
poetry update # Update dependencies
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Alternative: pip-tools
|
|
310
|
+
|
|
311
|
+
```
|
|
312
|
+
# requirements.in
|
|
313
|
+
requests>=2.31.0
|
|
314
|
+
pydantic>=2.0.0
|
|
315
|
+
|
|
316
|
+
# requirements-dev.in
|
|
317
|
+
-r requirements.in
|
|
318
|
+
pytest>=7.4.0
|
|
319
|
+
mypy>=1.5.0
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Commands:
|
|
323
|
+
```bash
|
|
324
|
+
pip-compile requirements.in
|
|
325
|
+
pip-compile requirements-dev.in
|
|
326
|
+
pip-sync requirements-dev.txt
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Dependency Guidelines
|
|
330
|
+
|
|
331
|
+
1. **Check for latest versions**:
|
|
332
|
+
- Use Context7 MCP tool if available
|
|
333
|
+
- Check PyPI: `pip index versions <package>`
|
|
334
|
+
- Review changelog for breaking changes
|
|
335
|
+
|
|
336
|
+
2. **Version pinning**:
|
|
337
|
+
- ✅ Pin exact versions in applications
|
|
338
|
+
- ✅ Use ranges in libraries (`>=1.0,<2.0`)
|
|
339
|
+
- ✅ Keep dependencies updated regularly
|
|
340
|
+
- ❌ Don't use outdated packages with security issues
|
|
341
|
+
|
|
342
|
+
## Error Handling
|
|
343
|
+
|
|
344
|
+
- Use specific exception types
|
|
345
|
+
- Create custom exceptions when needed
|
|
346
|
+
- Document exceptions in docstrings
|
|
347
|
+
- Never use bare `except:`
|
|
348
|
+
|
|
349
|
+
Example:
|
|
350
|
+
```python
|
|
351
|
+
class ValidationError(Exception):
|
|
352
|
+
"""Raised when data validation fails."""
|
|
353
|
+
|
|
354
|
+
def __init__(self, message: str, field: str):
|
|
355
|
+
super().__init__(message)
|
|
356
|
+
self.field = field
|
|
357
|
+
|
|
358
|
+
def validate_data(data: dict[str, Any]) -> None:
|
|
359
|
+
"""
|
|
360
|
+
Validate input data.
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
data: The data to validate
|
|
364
|
+
|
|
365
|
+
Raises:
|
|
366
|
+
ValidationError: If validation fails
|
|
367
|
+
"""
|
|
368
|
+
if not isinstance(data, dict):
|
|
369
|
+
raise ValidationError("Data must be a dictionary", "data")
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Documentation
|
|
373
|
+
|
|
374
|
+
- **Docstrings**: Google or NumPy style
|
|
375
|
+
- **Type hints**: Required for public APIs
|
|
376
|
+
- **README**: Include installation and usage
|
|
377
|
+
- **API docs**: Consider Sphinx for large projects
|
|
378
|
+
|
|
379
|
+
Example (Google style):
|
|
380
|
+
```python
|
|
381
|
+
def process_data(input_data: str, options: dict[str, Any] | None = None) -> list[str]:
|
|
382
|
+
"""
|
|
383
|
+
Process input data and return results.
|
|
384
|
+
|
|
385
|
+
Args:
|
|
386
|
+
input_data: The input string to process
|
|
387
|
+
options: Optional processing options
|
|
388
|
+
|
|
389
|
+
Returns:
|
|
390
|
+
A list of processed strings
|
|
391
|
+
|
|
392
|
+
Raises:
|
|
393
|
+
ValidationError: If input_data is empty
|
|
394
|
+
|
|
395
|
+
Examples:
|
|
396
|
+
>>> process_data("hello")
|
|
397
|
+
['HELLO']
|
|
398
|
+
>>> process_data("world", {"lowercase": True})
|
|
399
|
+
['world']
|
|
400
|
+
"""
|
|
401
|
+
# Implementation
|
|
402
|
+
return []
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## Project Structure
|
|
406
|
+
|
|
407
|
+
```
|
|
408
|
+
project/
|
|
409
|
+
├── pyproject.toml # Project metadata and dependencies
|
|
410
|
+
├── README.md # Project overview (allowed in root)
|
|
411
|
+
├── CHANGELOG.md # Version history (allowed in root)
|
|
412
|
+
├── AGENTS.md # AI assistant rules (allowed in root)
|
|
413
|
+
├── LICENSE # Project license (allowed in root)
|
|
414
|
+
├── CONTRIBUTING.md # Contribution guidelines (allowed in root)
|
|
415
|
+
├── CODE_OF_CONDUCT.md # Code of conduct (allowed in root)
|
|
416
|
+
├── SECURITY.md # Security policy (allowed in root)
|
|
417
|
+
├── src/
|
|
418
|
+
│ └── mypackage/
|
|
419
|
+
│ ├── __init__.py
|
|
420
|
+
│ ├── module.py
|
|
421
|
+
│ └── py.typed # PEP 561 marker for type hints
|
|
422
|
+
├── tests/ # Test files
|
|
423
|
+
│ ├── __init__.py
|
|
424
|
+
│ └── test_module.py
|
|
425
|
+
└── docs/ # Documentation
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
## Async Programming
|
|
429
|
+
|
|
430
|
+
- Use `asyncio` for async code
|
|
431
|
+
- Type hints: `async def func() -> Coroutine`
|
|
432
|
+
- Testing: Use `pytest-asyncio`
|
|
433
|
+
|
|
434
|
+
Example:
|
|
435
|
+
```python
|
|
436
|
+
import asyncio
|
|
437
|
+
from typing import List
|
|
438
|
+
|
|
439
|
+
async def fetch_data(url: str) -> dict[str, Any]:
|
|
440
|
+
"""Fetch data asynchronously."""
|
|
441
|
+
# Implementation
|
|
442
|
+
return {}
|
|
443
|
+
|
|
444
|
+
async def main() -> None:
|
|
445
|
+
"""Main async function."""
|
|
446
|
+
results = await asyncio.gather(
|
|
447
|
+
fetch_data("url1"),
|
|
448
|
+
fetch_data("url2"),
|
|
449
|
+
)
|
|
450
|
+
print(results)
|
|
451
|
+
|
|
452
|
+
if __name__ == "__main__":
|
|
453
|
+
asyncio.run(main())
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
## CI/CD Requirements
|
|
457
|
+
|
|
458
|
+
Must include GitHub Actions workflows for:
|
|
459
|
+
|
|
460
|
+
1. **Testing** (`python-test.yml`):
|
|
461
|
+
- Test on ubuntu-latest, windows-latest, macos-latest
|
|
462
|
+
- Test on Python 3.11, 3.12
|
|
463
|
+
- Upload coverage reports
|
|
464
|
+
|
|
465
|
+
2. **Linting** (`python-lint.yml`):
|
|
466
|
+
- Format check: `ruff format --check .`
|
|
467
|
+
- Lint: `ruff check .`
|
|
468
|
+
- Type check: `mypy .`
|
|
469
|
+
|
|
470
|
+
3. **Security** (`python-security.yml`):
|
|
471
|
+
- Check for vulnerabilities: `pip-audit`
|
|
472
|
+
- Scan dependencies: `safety check`
|
|
473
|
+
|
|
474
|
+
## Package Publication
|
|
475
|
+
|
|
476
|
+
### Publishing to PyPI
|
|
477
|
+
|
|
478
|
+
**Prerequisites:**
|
|
479
|
+
1. Create account at https://pypi.org
|
|
480
|
+
2. Enable 2FA for security
|
|
481
|
+
3. Configure trusted publishing (recommended) or create API token
|
|
482
|
+
4. For trusted publishing: Add GitHub as publisher in PyPI settings
|
|
483
|
+
|
|
484
|
+
**pyproject.toml Configuration:**
|
|
485
|
+
|
|
486
|
+
```toml
|
|
487
|
+
[build-system]
|
|
488
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
489
|
+
build-backend = "setuptools.build_meta"
|
|
490
|
+
|
|
491
|
+
[project]
|
|
492
|
+
name = "your-package-name"
|
|
493
|
+
version = "1.0.0"
|
|
494
|
+
description = "A short description of your package"
|
|
495
|
+
readme = "README.md"
|
|
496
|
+
requires-python = ">=3.11"
|
|
497
|
+
license = {text = "MIT"}
|
|
498
|
+
authors = [
|
|
499
|
+
{name = "Your Name", email = "your.email@example.com"}
|
|
500
|
+
]
|
|
501
|
+
keywords = ["your", "keywords"]
|
|
502
|
+
classifiers = [
|
|
503
|
+
"Development Status :: 4 - Beta",
|
|
504
|
+
"Intended Audience :: Developers",
|
|
505
|
+
"License :: OSI Approved :: MIT License",
|
|
506
|
+
"Programming Language :: Python :: 3",
|
|
507
|
+
"Programming Language :: Python :: 3.11",
|
|
508
|
+
"Programming Language :: Python :: 3.12",
|
|
509
|
+
]
|
|
510
|
+
dependencies = [
|
|
511
|
+
"requests>=2.31.0",
|
|
512
|
+
]
|
|
513
|
+
|
|
514
|
+
[project.optional-dependencies]
|
|
515
|
+
dev = [
|
|
516
|
+
"pytest>=7.4.0",
|
|
517
|
+
"pytest-cov>=4.1.0",
|
|
518
|
+
"ruff>=0.1.0",
|
|
519
|
+
"mypy>=1.7.0",
|
|
520
|
+
"black>=23.12.0",
|
|
521
|
+
]
|
|
522
|
+
|
|
523
|
+
[project.urls]
|
|
524
|
+
Homepage = "https://github.com/your-org/your-package"
|
|
525
|
+
Documentation = "https://your-package.readthedocs.io"
|
|
526
|
+
Repository = "https://github.com/your-org/your-package"
|
|
527
|
+
"Bug Tracker" = "https://github.com/your-org/your-package/issues"
|
|
528
|
+
|
|
529
|
+
[tool.setuptools.packages.find]
|
|
530
|
+
where = ["src"]
|
|
531
|
+
|
|
532
|
+
[tool.setuptools.package-data]
|
|
533
|
+
your_package = ["py.typed"]
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### PEP 625 Package Naming Convention
|
|
537
|
+
|
|
538
|
+
**CRITICAL**: Package names must be normalized according to PEP 625.
|
|
539
|
+
|
|
540
|
+
PyPI requires source distribution filenames to use normalized package names (underscores instead of hyphens).
|
|
541
|
+
|
|
542
|
+
**Naming Rules:**
|
|
543
|
+
|
|
544
|
+
1. **Package name in `pyproject.toml`**: Use underscores (`_`)
|
|
545
|
+
```toml
|
|
546
|
+
[project]
|
|
547
|
+
name = "my_package_name" # Correct
|
|
548
|
+
# NOT: name = "my-package-name" # Will cause deprecation warning
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
2. **Package directory**: Must match with underscores
|
|
552
|
+
```
|
|
553
|
+
src/
|
|
554
|
+
└── my_package_name/ # Correct
|
|
555
|
+
├── __init__.py
|
|
556
|
+
└── ...
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
3. **Import statement**: Uses underscores
|
|
560
|
+
```python
|
|
561
|
+
import my_package_name
|
|
562
|
+
from my_package_name import something
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
4. **Distribution filename**: Will be `my_package_name-1.0.0.tar.gz` ✅
|
|
566
|
+
|
|
567
|
+
**Common Issue:**
|
|
568
|
+
|
|
569
|
+
If you use hyphens in the package name, PyPI will reject new uploads:
|
|
570
|
+
```toml
|
|
571
|
+
# ❌ WRONG - Will fail PEP 625 compliance
|
|
572
|
+
[project]
|
|
573
|
+
name = "my-package-name"
|
|
574
|
+
|
|
575
|
+
# Result: my-package-name-1.0.0.tar.gz (non-compliant)
|
|
576
|
+
# PyPI Error: "Filename does not contain normalized project name"
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
**Correct Approach:**
|
|
580
|
+
```toml
|
|
581
|
+
# ✅ CORRECT - PEP 625 compliant
|
|
582
|
+
[project]
|
|
583
|
+
name = "my_package_name"
|
|
584
|
+
|
|
585
|
+
# Result: my_package_name-1.0.0.tar.gz (compliant)
|
|
586
|
+
# PyPI: Accepts upload without warnings
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
**Migration from Hyphenated Names:**
|
|
590
|
+
|
|
591
|
+
If you previously published with hyphens:
|
|
592
|
+
|
|
593
|
+
1. Update `pyproject.toml` and `setup.py` to use underscores
|
|
594
|
+
2. Existing uploads remain on PyPI (no action needed)
|
|
595
|
+
3. Future uploads will use normalized name
|
|
596
|
+
4. PyPI will automatically redirect:
|
|
597
|
+
- `pip install my-package-name` → works (auto-normalized)
|
|
598
|
+
- `pip install my_package_name` → works (canonical form)
|
|
599
|
+
5. Import statement unchanged: `import my_package_name`
|
|
600
|
+
|
|
601
|
+
**Reference**: [PEP 625 - File name of a Source Distribution](https://peps.python.org/pep-0625/)
|
|
602
|
+
|
|
603
|
+
**Publishing Workflow:**
|
|
604
|
+
|
|
605
|
+
1. Update version in pyproject.toml
|
|
606
|
+
2. Update CHANGELOG.md
|
|
607
|
+
3. Run quality checks:
|
|
608
|
+
```bash
|
|
609
|
+
ruff check .
|
|
610
|
+
ruff format --check .
|
|
611
|
+
mypy .
|
|
612
|
+
pytest
|
|
613
|
+
```
|
|
614
|
+
4. Build package:
|
|
615
|
+
```bash
|
|
616
|
+
python -m build
|
|
617
|
+
twine check dist/*
|
|
618
|
+
```
|
|
619
|
+
5. Test on Test PyPI (optional):
|
|
620
|
+
```bash
|
|
621
|
+
twine upload --repository testpypi dist/*
|
|
622
|
+
```
|
|
623
|
+
6. Create git tag: `git tag v1.0.0 && git push --tags`
|
|
624
|
+
7. GitHub Actions automatically publishes to PyPI
|
|
625
|
+
8. Or manual publish: `twine upload dist/*`
|
|
626
|
+
|
|
627
|
+
**Publishing Checklist:**
|
|
628
|
+
|
|
629
|
+
- ✅ All tests passing (`pytest`)
|
|
630
|
+
- ✅ Type checking passes (`mypy .`)
|
|
631
|
+
- ✅ Linting passes (`ruff check .`)
|
|
632
|
+
- ✅ Code formatted (`ruff format .`)
|
|
633
|
+
- ✅ Version updated in pyproject.toml
|
|
634
|
+
- ✅ CHANGELOG.md updated
|
|
635
|
+
- ✅ README.md up to date
|
|
636
|
+
- ✅ LICENSE file present
|
|
637
|
+
- ✅ **Package name uses underscores (PEP 625 compliant)**
|
|
638
|
+
- ✅ `py.typed` marker for type hints
|
|
639
|
+
- ✅ Package builds successfully (`python -m build`)
|
|
640
|
+
- ✅ Package checks pass (`twine check dist/*`)
|
|
641
|
+
- ✅ Manifest complete (`check-manifest`)
|
|
642
|
+
- ✅ **Verify dist filename**: `my_package-1.0.0.tar.gz` (underscores) ✅
|
|
643
|
+
|
|
644
|
+
**Trusted Publishing (Recommended):**
|
|
645
|
+
|
|
646
|
+
PyPI trusted publishing eliminates the need for API tokens:
|
|
647
|
+
|
|
648
|
+
1. Go to PyPI → Your Account → Publishing
|
|
649
|
+
2. Add a new pending publisher:
|
|
650
|
+
- PyPI Project Name: `your-package-name`
|
|
651
|
+
- Owner: `your-github-org`
|
|
652
|
+
- Repository: `your-repo-name`
|
|
653
|
+
- Workflow: `python-publish.yml`
|
|
654
|
+
- Environment: `release` (optional)
|
|
655
|
+
|
|
656
|
+
3. GitHub Actions will authenticate automatically using OIDC
|
|
657
|
+
|
|
658
|
+
**Versioning:**
|
|
659
|
+
|
|
660
|
+
Use semantic versioning and consider:
|
|
661
|
+
- **Automated versioning**: Use tools like `bump2version` or `setuptools_scm`
|
|
662
|
+
- **Version from git tags**: Configure `setuptools_scm` in pyproject.toml:
|
|
663
|
+
|
|
664
|
+
```toml
|
|
665
|
+
[build-system]
|
|
666
|
+
requires = ["setuptools>=68.0", "setuptools_scm>=8.0"]
|
|
667
|
+
|
|
668
|
+
[tool.setuptools_scm]
|
|
669
|
+
version_file = "src/your_package/_version.py"
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
**Type Hints:**
|
|
673
|
+
|
|
674
|
+
Include `py.typed` marker for PEP 561 compliance:
|
|
675
|
+
```bash
|
|
676
|
+
touch src/your_package/py.typed
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
This tells type checkers your package includes type information.
|
|
680
|
+
|
|
681
|
+
<!-- PYTHON:END -->
|
|
682
|
+
|