erk 0.4.5__py3-none-any.whl
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.
- erk/__init__.py +12 -0
- erk/__main__.py +6 -0
- erk/agent_docs/__init__.py +5 -0
- erk/agent_docs/models.py +123 -0
- erk/agent_docs/operations.py +666 -0
- erk/artifacts/__init__.py +5 -0
- erk/artifacts/artifact_health.py +623 -0
- erk/artifacts/detection.py +16 -0
- erk/artifacts/discovery.py +343 -0
- erk/artifacts/models.py +63 -0
- erk/artifacts/staleness.py +56 -0
- erk/artifacts/state.py +100 -0
- erk/artifacts/sync.py +624 -0
- erk/cli/__init__.py +0 -0
- erk/cli/activation.py +132 -0
- erk/cli/alias.py +53 -0
- erk/cli/cli.py +221 -0
- erk/cli/commands/__init__.py +0 -0
- erk/cli/commands/admin.py +153 -0
- erk/cli/commands/artifact/__init__.py +1 -0
- erk/cli/commands/artifact/check.py +260 -0
- erk/cli/commands/artifact/group.py +31 -0
- erk/cli/commands/artifact/list_cmd.py +89 -0
- erk/cli/commands/artifact/show.py +62 -0
- erk/cli/commands/artifact/sync_cmd.py +39 -0
- erk/cli/commands/branch/__init__.py +26 -0
- erk/cli/commands/branch/assign_cmd.py +152 -0
- erk/cli/commands/branch/checkout_cmd.py +357 -0
- erk/cli/commands/branch/create_cmd.py +161 -0
- erk/cli/commands/branch/list_cmd.py +82 -0
- erk/cli/commands/branch/unassign_cmd.py +197 -0
- erk/cli/commands/cc/__init__.py +15 -0
- erk/cli/commands/cc/jsonl_cmd.py +20 -0
- erk/cli/commands/cc/session/AGENTS.md +30 -0
- erk/cli/commands/cc/session/CLAUDE.md +1 -0
- erk/cli/commands/cc/session/__init__.py +15 -0
- erk/cli/commands/cc/session/list_cmd.py +167 -0
- erk/cli/commands/cc/session/show_cmd.py +175 -0
- erk/cli/commands/completion.py +89 -0
- erk/cli/commands/completions.py +165 -0
- erk/cli/commands/config.py +327 -0
- erk/cli/commands/docs/__init__.py +1 -0
- erk/cli/commands/docs/group.py +16 -0
- erk/cli/commands/docs/sync.py +121 -0
- erk/cli/commands/docs/validate.py +102 -0
- erk/cli/commands/doctor.py +243 -0
- erk/cli/commands/down.py +171 -0
- erk/cli/commands/exec/__init__.py +1 -0
- erk/cli/commands/exec/group.py +164 -0
- erk/cli/commands/exec/scripts/AGENTS.md +79 -0
- erk/cli/commands/exec/scripts/CLAUDE.md +1 -0
- erk/cli/commands/exec/scripts/__init__.py +5 -0
- erk/cli/commands/exec/scripts/add_reaction_to_comment.py +69 -0
- erk/cli/commands/exec/scripts/add_remote_execution_note.py +68 -0
- erk/cli/commands/exec/scripts/check_impl.py +152 -0
- erk/cli/commands/exec/scripts/ci_update_pr_body.py +294 -0
- erk/cli/commands/exec/scripts/create_extraction_branch.py +138 -0
- erk/cli/commands/exec/scripts/create_extraction_plan.py +242 -0
- erk/cli/commands/exec/scripts/create_issue_from_session.py +103 -0
- erk/cli/commands/exec/scripts/create_plan_from_context.py +103 -0
- erk/cli/commands/exec/scripts/create_worker_impl_from_issue.py +93 -0
- erk/cli/commands/exec/scripts/detect_trunk_branch.py +121 -0
- erk/cli/commands/exec/scripts/exit_plan_mode_hook.py +777 -0
- erk/cli/commands/exec/scripts/extract_latest_plan.py +49 -0
- erk/cli/commands/exec/scripts/extract_session_from_issue.py +150 -0
- erk/cli/commands/exec/scripts/find_project_dir.py +214 -0
- erk/cli/commands/exec/scripts/generate_pr_summary.py +112 -0
- erk/cli/commands/exec/scripts/get_closing_text.py +98 -0
- erk/cli/commands/exec/scripts/get_embedded_prompt.py +62 -0
- erk/cli/commands/exec/scripts/get_plan_metadata.py +95 -0
- erk/cli/commands/exec/scripts/get_pr_body_footer.py +70 -0
- erk/cli/commands/exec/scripts/get_pr_discussion_comments.py +149 -0
- erk/cli/commands/exec/scripts/get_pr_review_comments.py +155 -0
- erk/cli/commands/exec/scripts/impl_init.py +158 -0
- erk/cli/commands/exec/scripts/impl_signal.py +375 -0
- erk/cli/commands/exec/scripts/impl_verify.py +49 -0
- erk/cli/commands/exec/scripts/issue_title_to_filename.py +34 -0
- erk/cli/commands/exec/scripts/list_sessions.py +296 -0
- erk/cli/commands/exec/scripts/mark_impl_ended.py +188 -0
- erk/cli/commands/exec/scripts/mark_impl_started.py +188 -0
- erk/cli/commands/exec/scripts/marker.py +163 -0
- erk/cli/commands/exec/scripts/objective_save_to_issue.py +109 -0
- erk/cli/commands/exec/scripts/plan_save_to_issue.py +269 -0
- erk/cli/commands/exec/scripts/plan_update_issue.py +147 -0
- erk/cli/commands/exec/scripts/post_extraction_comment.py +237 -0
- erk/cli/commands/exec/scripts/post_or_update_pr_summary.py +133 -0
- erk/cli/commands/exec/scripts/post_pr_inline_comment.py +143 -0
- erk/cli/commands/exec/scripts/post_workflow_started_comment.py +168 -0
- erk/cli/commands/exec/scripts/preprocess_session.py +777 -0
- erk/cli/commands/exec/scripts/quick_submit.py +32 -0
- erk/cli/commands/exec/scripts/rebase_with_conflict_resolution.py +260 -0
- erk/cli/commands/exec/scripts/reply_to_discussion_comment.py +173 -0
- erk/cli/commands/exec/scripts/resolve_review_thread.py +170 -0
- erk/cli/commands/exec/scripts/session_id_injector_hook.py +52 -0
- erk/cli/commands/exec/scripts/setup_impl_from_issue.py +159 -0
- erk/cli/commands/exec/scripts/slot_objective.py +102 -0
- erk/cli/commands/exec/scripts/tripwires_reminder_hook.py +20 -0
- erk/cli/commands/exec/scripts/update_dispatch_info.py +116 -0
- erk/cli/commands/exec/scripts/user_prompt_hook.py +113 -0
- erk/cli/commands/exec/scripts/validate_plan_content.py +98 -0
- erk/cli/commands/exec/scripts/wrap_plan_in_metadata_block.py +34 -0
- erk/cli/commands/implement.py +695 -0
- erk/cli/commands/implement_shared.py +649 -0
- erk/cli/commands/info/__init__.py +14 -0
- erk/cli/commands/info/release_notes_cmd.py +128 -0
- erk/cli/commands/init.py +801 -0
- erk/cli/commands/land_cmd.py +690 -0
- erk/cli/commands/log_cmd.py +137 -0
- erk/cli/commands/md/__init__.py +5 -0
- erk/cli/commands/md/check.py +118 -0
- erk/cli/commands/md/group.py +14 -0
- erk/cli/commands/navigation_helpers.py +430 -0
- erk/cli/commands/objective/__init__.py +16 -0
- erk/cli/commands/objective/list_cmd.py +47 -0
- erk/cli/commands/objective_helpers.py +132 -0
- erk/cli/commands/plan/__init__.py +32 -0
- erk/cli/commands/plan/check_cmd.py +174 -0
- erk/cli/commands/plan/close_cmd.py +69 -0
- erk/cli/commands/plan/create_cmd.py +120 -0
- erk/cli/commands/plan/docs/__init__.py +18 -0
- erk/cli/commands/plan/docs/extract_cmd.py +53 -0
- erk/cli/commands/plan/docs/unextract_cmd.py +38 -0
- erk/cli/commands/plan/docs/unextracted_cmd.py +72 -0
- erk/cli/commands/plan/extraction/__init__.py +16 -0
- erk/cli/commands/plan/extraction/complete_cmd.py +101 -0
- erk/cli/commands/plan/extraction/create_raw_cmd.py +63 -0
- erk/cli/commands/plan/get.py +71 -0
- erk/cli/commands/plan/list_cmd.py +754 -0
- erk/cli/commands/plan/log_cmd.py +440 -0
- erk/cli/commands/plan/start_cmd.py +459 -0
- erk/cli/commands/planner/__init__.py +40 -0
- erk/cli/commands/planner/configure_cmd.py +73 -0
- erk/cli/commands/planner/connect_cmd.py +96 -0
- erk/cli/commands/planner/create_cmd.py +148 -0
- erk/cli/commands/planner/list_cmd.py +51 -0
- erk/cli/commands/planner/register_cmd.py +105 -0
- erk/cli/commands/planner/set_default_cmd.py +23 -0
- erk/cli/commands/planner/unregister_cmd.py +43 -0
- erk/cli/commands/pr/__init__.py +23 -0
- erk/cli/commands/pr/check_cmd.py +112 -0
- erk/cli/commands/pr/checkout_cmd.py +165 -0
- erk/cli/commands/pr/fix_conflicts_cmd.py +82 -0
- erk/cli/commands/pr/parse_pr_reference.py +10 -0
- erk/cli/commands/pr/submit_cmd.py +360 -0
- erk/cli/commands/pr/sync_cmd.py +181 -0
- erk/cli/commands/prepare_cwd_recovery.py +60 -0
- erk/cli/commands/project/__init__.py +16 -0
- erk/cli/commands/project/init_cmd.py +91 -0
- erk/cli/commands/run/__init__.py +17 -0
- erk/cli/commands/run/list_cmd.py +189 -0
- erk/cli/commands/run/logs_cmd.py +54 -0
- erk/cli/commands/run/shared.py +19 -0
- erk/cli/commands/shell_integration.py +29 -0
- erk/cli/commands/slot/__init__.py +23 -0
- erk/cli/commands/slot/check_cmd.py +277 -0
- erk/cli/commands/slot/common.py +314 -0
- erk/cli/commands/slot/init_pool_cmd.py +157 -0
- erk/cli/commands/slot/list_cmd.py +228 -0
- erk/cli/commands/slot/repair_cmd.py +190 -0
- erk/cli/commands/stack/__init__.py +23 -0
- erk/cli/commands/stack/consolidate_cmd.py +470 -0
- erk/cli/commands/stack/list_cmd.py +79 -0
- erk/cli/commands/stack/move_cmd.py +309 -0
- erk/cli/commands/stack/split_old/README.md +64 -0
- erk/cli/commands/stack/split_old/__init__.py +5 -0
- erk/cli/commands/stack/split_old/command.py +233 -0
- erk/cli/commands/stack/split_old/display.py +116 -0
- erk/cli/commands/stack/split_old/plan.py +216 -0
- erk/cli/commands/status.py +58 -0
- erk/cli/commands/submit.py +768 -0
- erk/cli/commands/up.py +154 -0
- erk/cli/commands/upgrade.py +82 -0
- erk/cli/commands/wt/__init__.py +29 -0
- erk/cli/commands/wt/checkout_cmd.py +110 -0
- erk/cli/commands/wt/create_cmd.py +998 -0
- erk/cli/commands/wt/current_cmd.py +35 -0
- erk/cli/commands/wt/delete_cmd.py +573 -0
- erk/cli/commands/wt/list_cmd.py +332 -0
- erk/cli/commands/wt/rename_cmd.py +66 -0
- erk/cli/config.py +242 -0
- erk/cli/constants.py +29 -0
- erk/cli/core.py +65 -0
- erk/cli/debug.py +9 -0
- erk/cli/ensure-conversion-tasks.md +288 -0
- erk/cli/ensure.py +628 -0
- erk/cli/github_parsing.py +96 -0
- erk/cli/graphite.py +81 -0
- erk/cli/graphite_command.py +80 -0
- erk/cli/help_formatter.py +345 -0
- erk/cli/output.py +361 -0
- erk/cli/presets/dagster.toml +12 -0
- erk/cli/presets/generic.toml +12 -0
- erk/cli/prompt_hooks_templates/README.md +68 -0
- erk/cli/script_output.py +32 -0
- erk/cli/shell_integration/bash_wrapper.sh +32 -0
- erk/cli/shell_integration/fish_wrapper.fish +39 -0
- erk/cli/shell_integration/handler.py +338 -0
- erk/cli/shell_integration/zsh_wrapper.sh +32 -0
- erk/cli/shell_utils.py +171 -0
- erk/cli/subprocess_utils.py +92 -0
- erk/cli/uvx_detection.py +59 -0
- erk/core/__init__.py +0 -0
- erk/core/claude_executor.py +511 -0
- erk/core/claude_settings.py +317 -0
- erk/core/command_log.py +406 -0
- erk/core/commit_message_generator.py +234 -0
- erk/core/completion.py +10 -0
- erk/core/consolidation_utils.py +177 -0
- erk/core/context.py +570 -0
- erk/core/display/__init__.py +4 -0
- erk/core/display/abc.py +24 -0
- erk/core/display/real.py +30 -0
- erk/core/display_utils.py +526 -0
- erk/core/file_utils.py +87 -0
- erk/core/health_checks.py +1315 -0
- erk/core/health_checks_dogfooder/__init__.py +85 -0
- erk/core/health_checks_dogfooder/deprecated_dot_agent_config.py +64 -0
- erk/core/health_checks_dogfooder/legacy_claude_docs.py +69 -0
- erk/core/health_checks_dogfooder/legacy_config_locations.py +122 -0
- erk/core/health_checks_dogfooder/legacy_erk_docs_agent.py +61 -0
- erk/core/health_checks_dogfooder/legacy_erk_kits_folder.py +60 -0
- erk/core/health_checks_dogfooder/legacy_hook_settings.py +104 -0
- erk/core/health_checks_dogfooder/legacy_kit_yaml.py +78 -0
- erk/core/health_checks_dogfooder/legacy_kits_toml.py +43 -0
- erk/core/health_checks_dogfooder/outdated_erk_skill.py +43 -0
- erk/core/implementation_queue/__init__.py +1 -0
- erk/core/implementation_queue/github/__init__.py +8 -0
- erk/core/implementation_queue/github/abc.py +7 -0
- erk/core/implementation_queue/github/noop.py +38 -0
- erk/core/implementation_queue/github/printing.py +43 -0
- erk/core/implementation_queue/github/real.py +119 -0
- erk/core/init_utils.py +227 -0
- erk/core/output_filter.py +338 -0
- erk/core/plan_store/__init__.py +6 -0
- erk/core/planner/__init__.py +1 -0
- erk/core/planner/registry_abc.py +8 -0
- erk/core/planner/registry_fake.py +129 -0
- erk/core/planner/registry_real.py +195 -0
- erk/core/planner/types.py +7 -0
- erk/core/pr_utils.py +30 -0
- erk/core/release_notes.py +263 -0
- erk/core/repo_discovery.py +126 -0
- erk/core/script_writer.py +41 -0
- erk/core/services/__init__.py +1 -0
- erk/core/services/plan_list_service.py +94 -0
- erk/core/shell.py +51 -0
- erk/core/user_feedback.py +11 -0
- erk/core/version_check.py +55 -0
- erk/core/workflow_display.py +75 -0
- erk/core/worktree_pool.py +190 -0
- erk/core/worktree_utils.py +300 -0
- erk/data/CHANGELOG.md +438 -0
- erk/data/__init__.py +1 -0
- erk/data/claude/agents/devrun.md +180 -0
- erk/data/claude/commands/erk/__init__.py +0 -0
- erk/data/claude/commands/erk/create-extraction-plan.md +360 -0
- erk/data/claude/commands/erk/fix-conflicts.md +25 -0
- erk/data/claude/commands/erk/git-pr-push.md +345 -0
- erk/data/claude/commands/erk/implement-stacked-plan.md +96 -0
- erk/data/claude/commands/erk/land.md +193 -0
- erk/data/claude/commands/erk/objective-create.md +370 -0
- erk/data/claude/commands/erk/objective-list.md +34 -0
- erk/data/claude/commands/erk/objective-next-plan.md +220 -0
- erk/data/claude/commands/erk/objective-update-with-landed-pr.md +216 -0
- erk/data/claude/commands/erk/plan-implement.md +202 -0
- erk/data/claude/commands/erk/plan-save.md +45 -0
- erk/data/claude/commands/erk/plan-submit.md +39 -0
- erk/data/claude/commands/erk/pr-address.md +367 -0
- erk/data/claude/commands/erk/pr-submit.md +58 -0
- erk/data/claude/skills/dignified-python/SKILL.md +48 -0
- erk/data/claude/skills/dignified-python/cli-patterns.md +155 -0
- erk/data/claude/skills/dignified-python/dignified-python-core.md +1190 -0
- erk/data/claude/skills/dignified-python/subprocess.md +99 -0
- erk/data/claude/skills/dignified-python/versions/python-3.10.md +517 -0
- erk/data/claude/skills/dignified-python/versions/python-3.11.md +536 -0
- erk/data/claude/skills/dignified-python/versions/python-3.12.md +662 -0
- erk/data/claude/skills/dignified-python/versions/python-3.13.md +653 -0
- erk/data/claude/skills/erk-diff-analysis/SKILL.md +27 -0
- erk/data/claude/skills/erk-diff-analysis/references/commit-message-prompt.md +78 -0
- erk/data/claude/skills/learned-docs/SKILL.md +362 -0
- erk/data/github/actions/setup-claude-erk/action.yml +11 -0
- erk/data/github/prompts/dignified-python-review.md +125 -0
- erk/data/github/workflows/dignified-python-review.yml +61 -0
- erk/data/github/workflows/erk-impl.yml +251 -0
- erk/hooks/__init__.py +1 -0
- erk/hooks/decorators.py +319 -0
- erk/status/__init__.py +8 -0
- erk/status/collectors/__init__.py +9 -0
- erk/status/collectors/base.py +52 -0
- erk/status/collectors/git.py +76 -0
- erk/status/collectors/github.py +81 -0
- erk/status/collectors/graphite.py +80 -0
- erk/status/collectors/impl.py +145 -0
- erk/status/models/__init__.py +4 -0
- erk/status/models/status_data.py +404 -0
- erk/status/orchestrator.py +169 -0
- erk/status/renderers/__init__.py +5 -0
- erk/status/renderers/simple.py +322 -0
- erk/tui/AGENTS.md +193 -0
- erk/tui/CLAUDE.md +1 -0
- erk/tui/__init__.py +1 -0
- erk/tui/app.py +1404 -0
- erk/tui/commands/__init__.py +1 -0
- erk/tui/commands/executor.py +66 -0
- erk/tui/commands/provider.py +165 -0
- erk/tui/commands/real_executor.py +63 -0
- erk/tui/commands/registry.py +121 -0
- erk/tui/commands/types.py +36 -0
- erk/tui/data/__init__.py +1 -0
- erk/tui/data/provider.py +492 -0
- erk/tui/data/types.py +104 -0
- erk/tui/filtering/__init__.py +1 -0
- erk/tui/filtering/logic.py +43 -0
- erk/tui/filtering/types.py +55 -0
- erk/tui/jsonl_viewer/__init__.py +1 -0
- erk/tui/jsonl_viewer/app.py +61 -0
- erk/tui/jsonl_viewer/models.py +208 -0
- erk/tui/jsonl_viewer/widgets.py +204 -0
- erk/tui/sorting/__init__.py +6 -0
- erk/tui/sorting/logic.py +55 -0
- erk/tui/sorting/types.py +68 -0
- erk/tui/styles/dash.tcss +95 -0
- erk/tui/widgets/__init__.py +1 -0
- erk/tui/widgets/command_output.py +112 -0
- erk/tui/widgets/plan_table.py +276 -0
- erk/tui/widgets/status_bar.py +116 -0
- erk-0.4.5.dist-info/METADATA +376 -0
- erk-0.4.5.dist-info/RECORD +331 -0
- erk-0.4.5.dist-info/WHEEL +4 -0
- erk-0.4.5.dist-info/entry_points.txt +2 -0
- erk-0.4.5.dist-info/licenses/LICENSE.md +3 -0
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
---
|
|
2
|
+
---
|
|
3
|
+
|
|
4
|
+
# Type Annotations - Python 3.13
|
|
5
|
+
|
|
6
|
+
This document provides complete, canonical type annotation guidance for Python 3.13. Python 3.13 implements PEP 649 (Deferred Evaluation of Annotations), fundamentally changing how annotations are evaluated.
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
**The key change: forward references and circular imports work naturally without `from __future__ import annotations`.**
|
|
11
|
+
|
|
12
|
+
All type features from previous versions (3.10-3.12) continue to work.
|
|
13
|
+
|
|
14
|
+
**What's new in 3.13:**
|
|
15
|
+
|
|
16
|
+
- PEP 649 deferred annotation evaluation
|
|
17
|
+
- Forward references work naturally (no quotes, no `from __future__`)
|
|
18
|
+
- Circular imports no longer cause annotation errors
|
|
19
|
+
- **DO NOT use `from __future__ import annotations`**
|
|
20
|
+
|
|
21
|
+
**Available from 3.12:**
|
|
22
|
+
|
|
23
|
+
- PEP 695 type parameter syntax: `def func[T](x: T) -> T`
|
|
24
|
+
- `type` statement for better type aliases
|
|
25
|
+
|
|
26
|
+
**Available from 3.11:**
|
|
27
|
+
|
|
28
|
+
- `Self` type for self-returning methods
|
|
29
|
+
|
|
30
|
+
## Universal Philosophy
|
|
31
|
+
|
|
32
|
+
**Code Clarity:**
|
|
33
|
+
|
|
34
|
+
- Types serve as inline documentation
|
|
35
|
+
- Make function contracts explicit
|
|
36
|
+
- Reduce cognitive load when reading code
|
|
37
|
+
- Help understand data flow without tracing through implementation
|
|
38
|
+
|
|
39
|
+
**IDE Support:**
|
|
40
|
+
|
|
41
|
+
- Enable autocomplete and intelligent suggestions
|
|
42
|
+
- Catch typos and attribute errors before runtime
|
|
43
|
+
- Support refactoring tools (rename, move, extract)
|
|
44
|
+
- Provide jump-to-definition for typed objects
|
|
45
|
+
|
|
46
|
+
**Bug Prevention:**
|
|
47
|
+
|
|
48
|
+
- Catch type mismatches during static analysis
|
|
49
|
+
- Prevent None-related errors with explicit optional types
|
|
50
|
+
- Document expected input/output without running code
|
|
51
|
+
- Enable early detection of API contract violations
|
|
52
|
+
|
|
53
|
+
## Consistency Rules
|
|
54
|
+
|
|
55
|
+
**All public APIs:**
|
|
56
|
+
|
|
57
|
+
- 🔴 MUST: Type all function parameters (except `self` and `cls`)
|
|
58
|
+
- 🔴 MUST: Type all function return values
|
|
59
|
+
- 🔴 MUST: Type all class attributes
|
|
60
|
+
- 🟡 SHOULD: Type module-level constants
|
|
61
|
+
|
|
62
|
+
**Internal code:**
|
|
63
|
+
|
|
64
|
+
- 🟡 SHOULD: Type function signatures where helpful for clarity
|
|
65
|
+
- 🟢 MAY: Type complex local variables where type isn't obvious
|
|
66
|
+
- 🟢 MAY: Omit types for obvious cases (e.g., `count = 0`)
|
|
67
|
+
|
|
68
|
+
## Basic Collection Types
|
|
69
|
+
|
|
70
|
+
✅ **PREFERRED** - Use built-in generic types:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
names: list[str] = []
|
|
74
|
+
mapping: dict[str, int] = {}
|
|
75
|
+
unique_ids: set[str] = set()
|
|
76
|
+
coordinates: tuple[int, int] = (0, 0)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
❌ **WRONG** - Don't use typing module equivalents:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from typing import List, Dict, Set, Tuple # Don't do this
|
|
83
|
+
names: List[str] = []
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Why**: Built-in types are more concise, don't require imports, and are the modern Python standard (available since 3.10).
|
|
87
|
+
|
|
88
|
+
## Union Types
|
|
89
|
+
|
|
90
|
+
✅ **PREFERRED** - Use `|` operator:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
def process(value: str | int) -> str:
|
|
94
|
+
return str(value)
|
|
95
|
+
|
|
96
|
+
def find_config(name: str) -> dict[str, str] | dict[str, int]:
|
|
97
|
+
...
|
|
98
|
+
|
|
99
|
+
# Multiple unions
|
|
100
|
+
def parse(input: str | int | float) -> str:
|
|
101
|
+
return str(input)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
❌ **WRONG** - Don't use `typing.Union`:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from typing import Union
|
|
108
|
+
def process(value: Union[str, int]) -> str: # Don't do this
|
|
109
|
+
...
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Optional Types
|
|
113
|
+
|
|
114
|
+
✅ **PREFERRED** - Use `X | None`:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
def find_user(id: str) -> User | None:
|
|
118
|
+
"""Returns user or None if not found."""
|
|
119
|
+
if id in users:
|
|
120
|
+
return users[id]
|
|
121
|
+
return None
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
❌ **WRONG** - Don't use `typing.Optional`:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from typing import Optional
|
|
128
|
+
def find_user(id: str) -> Optional[User]: # Don't do this
|
|
129
|
+
...
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Callable Types
|
|
133
|
+
|
|
134
|
+
✅ **PREFERRED** - Use `collections.abc.Callable`:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from collections.abc import Callable
|
|
138
|
+
|
|
139
|
+
# Function that takes int, returns str
|
|
140
|
+
processor: Callable[[int], str] = str
|
|
141
|
+
|
|
142
|
+
# Function with no args, returns None
|
|
143
|
+
callback: Callable[[], None] = lambda: None
|
|
144
|
+
|
|
145
|
+
# Function with multiple args
|
|
146
|
+
validator: Callable[[str, int], bool] = lambda s, i: len(s) > i
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Interfaces: ABC vs Protocol
|
|
150
|
+
|
|
151
|
+
✅ **PREFERRED** - Use ABC for interfaces:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from abc import ABC, abstractmethod
|
|
155
|
+
|
|
156
|
+
class Repository(ABC):
|
|
157
|
+
@abstractmethod
|
|
158
|
+
def get(self, id: str) -> User | None:
|
|
159
|
+
"""Get user by ID."""
|
|
160
|
+
|
|
161
|
+
@abstractmethod
|
|
162
|
+
def save(self, user: User) -> None:
|
|
163
|
+
"""Save user."""
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
🟡 **VALID** - Use Protocol only for structural typing:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from typing import Protocol
|
|
170
|
+
|
|
171
|
+
class Drawable(Protocol):
|
|
172
|
+
def draw(self) -> None: ...
|
|
173
|
+
|
|
174
|
+
def render(obj: Drawable) -> None:
|
|
175
|
+
obj.draw()
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Dignified Python prefers ABC** because it makes inheritance and intent explicit.
|
|
179
|
+
|
|
180
|
+
## Self Type for Self-Returning Methods (3.11+)
|
|
181
|
+
|
|
182
|
+
✅ **PREFERRED** - Use Self for methods that return the instance:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from typing import Self
|
|
186
|
+
|
|
187
|
+
class Builder:
|
|
188
|
+
def set_name(self, name: str) -> Self:
|
|
189
|
+
self.name = name
|
|
190
|
+
return self
|
|
191
|
+
|
|
192
|
+
def set_value(self, value: int) -> Self:
|
|
193
|
+
self.value = value
|
|
194
|
+
return self
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Generic Functions with PEP 695 (3.12+)
|
|
198
|
+
|
|
199
|
+
✅ **PREFERRED** - Use PEP 695 type parameter syntax:
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
def first[T](items: list[T]) -> T | None:
|
|
203
|
+
"""Return first item or None if empty."""
|
|
204
|
+
if not items:
|
|
205
|
+
return None
|
|
206
|
+
return items[0]
|
|
207
|
+
|
|
208
|
+
def identity[T](value: T) -> T:
|
|
209
|
+
"""Return value unchanged."""
|
|
210
|
+
return value
|
|
211
|
+
|
|
212
|
+
# Multiple type parameters
|
|
213
|
+
def zip_dicts[K, V](keys: list[K], values: list[V]) -> dict[K, V]:
|
|
214
|
+
"""Create dict from separate key and value lists."""
|
|
215
|
+
return dict(zip(keys, values))
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
🟡 **VALID** - TypeVar still works:
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
from typing import TypeVar
|
|
222
|
+
|
|
223
|
+
T = TypeVar("T")
|
|
224
|
+
|
|
225
|
+
def first(items: list[T]) -> T | None:
|
|
226
|
+
if not items:
|
|
227
|
+
return None
|
|
228
|
+
return items[0]
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Note**: Prefer PEP 695 syntax for simple generics. TypeVar is still needed for constraints/bounds.
|
|
232
|
+
|
|
233
|
+
## Generic Classes with PEP 695 (3.12+)
|
|
234
|
+
|
|
235
|
+
✅ **PREFERRED** - Use PEP 695 class syntax:
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
class Stack[T]:
|
|
239
|
+
"""A generic stack data structure."""
|
|
240
|
+
|
|
241
|
+
def __init__(self) -> None:
|
|
242
|
+
self._items: list[T] = []
|
|
243
|
+
|
|
244
|
+
def push(self, item: T) -> Self:
|
|
245
|
+
self._items.append(item)
|
|
246
|
+
return self
|
|
247
|
+
|
|
248
|
+
def pop(self) -> T | None:
|
|
249
|
+
if not self._items:
|
|
250
|
+
return None
|
|
251
|
+
return self._items.pop()
|
|
252
|
+
|
|
253
|
+
# Usage
|
|
254
|
+
int_stack = Stack[int]()
|
|
255
|
+
int_stack.push(42).push(43)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
🟡 **VALID** - Generic with TypeVar still works:
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
from typing import Generic, TypeVar
|
|
262
|
+
|
|
263
|
+
T = TypeVar("T")
|
|
264
|
+
|
|
265
|
+
class Stack(Generic[T]):
|
|
266
|
+
def __init__(self) -> None:
|
|
267
|
+
self._items: list[T] = []
|
|
268
|
+
# ... rest of implementation
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**Note**: PEP 695 is cleaner - no imports needed, type parameter scope is local to class.
|
|
272
|
+
|
|
273
|
+
## Type Parameter Bounds (3.12+)
|
|
274
|
+
|
|
275
|
+
✅ **Use bounds with PEP 695**:
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
class Comparable:
|
|
279
|
+
def compare(self, other: object) -> int:
|
|
280
|
+
...
|
|
281
|
+
|
|
282
|
+
def max_value[T: Comparable](items: list[T]) -> T:
|
|
283
|
+
"""Get maximum value from comparable items."""
|
|
284
|
+
return max(items, key=lambda x: x)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Constrained TypeVars (Still Use TypeVar)
|
|
288
|
+
|
|
289
|
+
✅ **Use TypeVar for specific type constraints**:
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
from typing import TypeVar
|
|
293
|
+
|
|
294
|
+
# Constrained to specific types - must use TypeVar
|
|
295
|
+
Numeric = TypeVar("Numeric", int, float)
|
|
296
|
+
|
|
297
|
+
def add(a: Numeric, b: Numeric) -> Numeric:
|
|
298
|
+
return a + b
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
❌ **WRONG** - PEP 695 doesn't support constraints:
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
# This doesn't constrain to int|float
|
|
305
|
+
def add[Numeric](a: Numeric, b: Numeric) -> Numeric:
|
|
306
|
+
return a + b
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Type Aliases with type Statement (3.12+)
|
|
310
|
+
|
|
311
|
+
✅ **PREFERRED** - Use `type` statement:
|
|
312
|
+
|
|
313
|
+
```python
|
|
314
|
+
# Simple alias
|
|
315
|
+
type UserId = str
|
|
316
|
+
type Config = dict[str, str | int | bool]
|
|
317
|
+
|
|
318
|
+
# Generic type alias
|
|
319
|
+
type Result[T] = tuple[T, str | None]
|
|
320
|
+
|
|
321
|
+
def process(value: str) -> Result[int]:
|
|
322
|
+
try:
|
|
323
|
+
return (int(value), None)
|
|
324
|
+
except ValueError as e:
|
|
325
|
+
return (0, str(e))
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
🟡 **VALID** - Simple assignment still works:
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
UserId = str # Still valid
|
|
332
|
+
Config = dict[str, str | int | bool] # Still valid
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Note**: `type` statement is more explicit and works better with generics.
|
|
336
|
+
|
|
337
|
+
## Forward References and Circular Imports (NEW in 3.13)
|
|
338
|
+
|
|
339
|
+
✅ **CORRECT** - Just works naturally with PEP 649:
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
# Forward reference - no quotes needed!
|
|
343
|
+
class Node:
|
|
344
|
+
def __init__(self, value: int, parent: Node | None = None):
|
|
345
|
+
self.value = value
|
|
346
|
+
self.parent = parent
|
|
347
|
+
|
|
348
|
+
# Circular imports - just works!
|
|
349
|
+
# a.py
|
|
350
|
+
from b import B
|
|
351
|
+
|
|
352
|
+
class A:
|
|
353
|
+
def method(self) -> B:
|
|
354
|
+
...
|
|
355
|
+
|
|
356
|
+
# b.py
|
|
357
|
+
from a import A
|
|
358
|
+
|
|
359
|
+
class B:
|
|
360
|
+
def method(self) -> A:
|
|
361
|
+
...
|
|
362
|
+
|
|
363
|
+
# Recursive types - no future needed!
|
|
364
|
+
type JsonValue = dict[str, JsonValue] | list[JsonValue] | str | int | float | bool | None
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
❌ **WRONG** - Don't use `from __future__ import annotations`:
|
|
368
|
+
|
|
369
|
+
```python
|
|
370
|
+
from __future__ import annotations # DON'T DO THIS in Python 3.13
|
|
371
|
+
|
|
372
|
+
class Node:
|
|
373
|
+
def __init__(self, value: int, parent: Node | None = None):
|
|
374
|
+
...
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
**Why avoid `from __future__ import annotations` in 3.13:**
|
|
378
|
+
|
|
379
|
+
- Unnecessary - PEP 649 provides better default behavior
|
|
380
|
+
- Can cause confusion
|
|
381
|
+
- Masks the native 3.13 deferred evaluation
|
|
382
|
+
- Prevents you from leveraging improvements
|
|
383
|
+
|
|
384
|
+
## Complete Examples
|
|
385
|
+
|
|
386
|
+
### Tree Structure with Natural Forward References
|
|
387
|
+
|
|
388
|
+
```python
|
|
389
|
+
from typing import Self
|
|
390
|
+
from collections.abc import Callable
|
|
391
|
+
|
|
392
|
+
class Node[T]:
|
|
393
|
+
"""Tree node - forward reference works naturally in 3.13!"""
|
|
394
|
+
|
|
395
|
+
def __init__(
|
|
396
|
+
self,
|
|
397
|
+
value: T,
|
|
398
|
+
parent: Node[T] | None = None, # Forward ref, no quotes!
|
|
399
|
+
children: list[Node[T]] | None = None, # Forward ref, no quotes!
|
|
400
|
+
) -> None:
|
|
401
|
+
self.value = value
|
|
402
|
+
self.parent = parent
|
|
403
|
+
self.children = children or []
|
|
404
|
+
|
|
405
|
+
def add_child(self, child: Node[T]) -> Self:
|
|
406
|
+
"""Add child and return self for chaining."""
|
|
407
|
+
self.children.append(child)
|
|
408
|
+
child.parent = self
|
|
409
|
+
return self
|
|
410
|
+
|
|
411
|
+
def find(self, predicate: Callable[[T], bool]) -> Node[T] | None:
|
|
412
|
+
"""Find first node matching predicate."""
|
|
413
|
+
if predicate(self.value):
|
|
414
|
+
return self
|
|
415
|
+
|
|
416
|
+
for child in self.children:
|
|
417
|
+
result = child.find(predicate)
|
|
418
|
+
if result:
|
|
419
|
+
return result
|
|
420
|
+
|
|
421
|
+
return None
|
|
422
|
+
|
|
423
|
+
# Usage - all type-safe with no __future__ import!
|
|
424
|
+
root = Node[int](1)
|
|
425
|
+
root.add_child(Node[int](2)).add_child(Node[int](3))
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Generic Repository with PEP 695
|
|
429
|
+
|
|
430
|
+
```python
|
|
431
|
+
from abc import ABC, abstractmethod
|
|
432
|
+
from typing import Self
|
|
433
|
+
|
|
434
|
+
class Entity[T]:
|
|
435
|
+
"""Base class for entities."""
|
|
436
|
+
|
|
437
|
+
def __init__(self, id: T) -> None:
|
|
438
|
+
self.id = id
|
|
439
|
+
|
|
440
|
+
class Repository[T](ABC):
|
|
441
|
+
"""Generic repository interface."""
|
|
442
|
+
|
|
443
|
+
@abstractmethod
|
|
444
|
+
def get(self, id: str) -> T | None:
|
|
445
|
+
"""Get entity by ID."""
|
|
446
|
+
|
|
447
|
+
@abstractmethod
|
|
448
|
+
def save(self, entity: T) -> None:
|
|
449
|
+
"""Save entity."""
|
|
450
|
+
|
|
451
|
+
@abstractmethod
|
|
452
|
+
def delete(self, id: str) -> bool:
|
|
453
|
+
"""Delete entity, return True if deleted."""
|
|
454
|
+
|
|
455
|
+
class User(Entity[str]):
|
|
456
|
+
def __init__(self, id: str, name: str) -> None:
|
|
457
|
+
super().__init__(id)
|
|
458
|
+
self.name = name
|
|
459
|
+
|
|
460
|
+
class UserRepository(Repository[User]):
|
|
461
|
+
def __init__(self) -> None:
|
|
462
|
+
self._users: dict[str, User] = {}
|
|
463
|
+
|
|
464
|
+
def get(self, id: str) -> User | None:
|
|
465
|
+
if id not in self._users:
|
|
466
|
+
return None
|
|
467
|
+
return self._users[id]
|
|
468
|
+
|
|
469
|
+
def save(self, entity: User) -> None:
|
|
470
|
+
self._users[entity.id] = entity
|
|
471
|
+
|
|
472
|
+
def delete(self, id: str) -> bool:
|
|
473
|
+
if id not in self._users:
|
|
474
|
+
return False
|
|
475
|
+
del self._users[id]
|
|
476
|
+
return True
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
## General Best Practices
|
|
480
|
+
|
|
481
|
+
**Prefer specificity:**
|
|
482
|
+
|
|
483
|
+
```python
|
|
484
|
+
# ✅ GOOD - Specific
|
|
485
|
+
def get_config() -> dict[str, str | int]:
|
|
486
|
+
...
|
|
487
|
+
|
|
488
|
+
# ❌ WRONG - Too vague
|
|
489
|
+
def get_config() -> dict:
|
|
490
|
+
...
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
**Use Union sparingly:**
|
|
494
|
+
|
|
495
|
+
```python
|
|
496
|
+
# ✅ GOOD - Union only when necessary
|
|
497
|
+
def process(value: str | int) -> str:
|
|
498
|
+
...
|
|
499
|
+
|
|
500
|
+
# ❌ WRONG - Too permissive
|
|
501
|
+
def process(value: str | int | list | dict) -> str | None | list:
|
|
502
|
+
...
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
**Be explicit with None:**
|
|
506
|
+
|
|
507
|
+
```python
|
|
508
|
+
# ✅ GOOD - Explicit optional
|
|
509
|
+
def find_user(id: str) -> User | None:
|
|
510
|
+
...
|
|
511
|
+
|
|
512
|
+
# ❌ WRONG - Implicit None return
|
|
513
|
+
def find_user(id: str) -> User:
|
|
514
|
+
return None # Type checker error!
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
**Avoid Any when possible:**
|
|
518
|
+
|
|
519
|
+
```python
|
|
520
|
+
# ✅ GOOD - Specific type
|
|
521
|
+
def serialize(obj: User | Config) -> str:
|
|
522
|
+
...
|
|
523
|
+
|
|
524
|
+
# ❌ WRONG - Defeats purpose of types
|
|
525
|
+
from typing import Any
|
|
526
|
+
def serialize(obj: Any) -> str:
|
|
527
|
+
...
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
## When to Use Types
|
|
531
|
+
|
|
532
|
+
**Always type:**
|
|
533
|
+
|
|
534
|
+
- Public function signatures (parameters + return)
|
|
535
|
+
- Class attributes (including private ones)
|
|
536
|
+
- Function parameters that cross module boundaries
|
|
537
|
+
- Return values that aren't immediately obvious
|
|
538
|
+
|
|
539
|
+
**Type when helpful:**
|
|
540
|
+
|
|
541
|
+
- Complex local variables
|
|
542
|
+
- Closures and nested functions
|
|
543
|
+
- Lambda expressions used as callbacks
|
|
544
|
+
|
|
545
|
+
**Can skip:**
|
|
546
|
+
|
|
547
|
+
- Obvious cases: `count = 0`, `name = "example"`
|
|
548
|
+
- Trivial private helpers
|
|
549
|
+
- Test fixture setup code (if types add no clarity)
|
|
550
|
+
|
|
551
|
+
## Type Checking with ty
|
|
552
|
+
|
|
553
|
+
Dignified Python uses ty for static type checking:
|
|
554
|
+
|
|
555
|
+
```bash
|
|
556
|
+
# Check all files
|
|
557
|
+
ty check
|
|
558
|
+
|
|
559
|
+
# Check specific file
|
|
560
|
+
ty check src/mymodule.py
|
|
561
|
+
|
|
562
|
+
# Check with specific Python version
|
|
563
|
+
ty check --python-version 3.13
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**Configuration** (in `pyproject.toml`):
|
|
567
|
+
|
|
568
|
+
```toml
|
|
569
|
+
[tool.ty.environment]
|
|
570
|
+
python-version = "3.13"
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
## Anti-Patterns
|
|
574
|
+
|
|
575
|
+
**❌ Don't ignore type errors with `# type: ignore`**
|
|
576
|
+
|
|
577
|
+
```python
|
|
578
|
+
# ❌ WRONG - Hiding type error
|
|
579
|
+
result = unsafe_function() # type: ignore
|
|
580
|
+
|
|
581
|
+
# ✅ CORRECT - Fix the type error
|
|
582
|
+
result: Expected = cast(Expected, unsafe_function())
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
**❌ Don't use bare Exception in type hints**
|
|
586
|
+
|
|
587
|
+
```python
|
|
588
|
+
# ❌ WRONG - No value from typing exception
|
|
589
|
+
def risky() -> str | Exception:
|
|
590
|
+
...
|
|
591
|
+
|
|
592
|
+
# ✅ CORRECT - Let exceptions bubble
|
|
593
|
+
def risky() -> str:
|
|
594
|
+
... # Raises ValueError on error
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
**❌ Don't over-type simple cases**
|
|
598
|
+
|
|
599
|
+
```python
|
|
600
|
+
# ❌ WRONG - Obvious from context
|
|
601
|
+
def add_numbers(a: int, b: int) -> int:
|
|
602
|
+
result: int = a + b # Unnecessary type annotation
|
|
603
|
+
return result
|
|
604
|
+
|
|
605
|
+
# ✅ CORRECT - Type only signature
|
|
606
|
+
def add_numbers(a: int, b: int) -> int:
|
|
607
|
+
result = a + b # Type is obvious
|
|
608
|
+
return result
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
## Migration from 3.10/3.11
|
|
612
|
+
|
|
613
|
+
If migrating from Python 3.10/3.11:
|
|
614
|
+
|
|
615
|
+
1. **Remove `from __future__ import annotations`** - No longer needed
|
|
616
|
+
2. **Consider upgrading to PEP 695 syntax** - Cleaner generics
|
|
617
|
+
3. **Use `type` statement for aliases** - More explicit than assignment
|
|
618
|
+
4. **Remove quoted forward references** - They work naturally now
|
|
619
|
+
|
|
620
|
+
```python
|
|
621
|
+
# Python 3.10/3.11
|
|
622
|
+
from __future__ import annotations
|
|
623
|
+
from typing import TypeVar, Generic
|
|
624
|
+
|
|
625
|
+
T = TypeVar("T")
|
|
626
|
+
|
|
627
|
+
class Node(Generic[T]):
|
|
628
|
+
def __init__(self, value: T, parent: "Node[T] | None" = None):
|
|
629
|
+
...
|
|
630
|
+
|
|
631
|
+
# Python 3.13
|
|
632
|
+
from typing import Self
|
|
633
|
+
|
|
634
|
+
class Node[T]:
|
|
635
|
+
def __init__(self, value: T, parent: Node[T] | None = None):
|
|
636
|
+
...
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
## What typing imports are still needed?
|
|
640
|
+
|
|
641
|
+
**Very rare:**
|
|
642
|
+
|
|
643
|
+
- `TypeVar` - Only for constrained/bounded type variables
|
|
644
|
+
- `Any` - Use sparingly when type truly unknown
|
|
645
|
+
- `Protocol` - Structural typing (prefer ABC)
|
|
646
|
+
- `TYPE_CHECKING` - Conditional imports to avoid circular dependencies
|
|
647
|
+
|
|
648
|
+
**Never needed:**
|
|
649
|
+
|
|
650
|
+
- `List`, `Dict`, `Set`, `Tuple` - Use built-in types
|
|
651
|
+
- `Union` - Use `|` operator
|
|
652
|
+
- `Optional` - Use `X | None`
|
|
653
|
+
- `Generic` - Use PEP 695 class syntax
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: erk-diff-analysis
|
|
3
|
+
description: Internal skill for commit message generation. Only load when explicitly requested by name or invoked by commands.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Diff Analysis
|
|
7
|
+
|
|
8
|
+
This skill provides the commit message generation prompt used by PR submission commands.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
Only load this skill when:
|
|
13
|
+
|
|
14
|
+
- Explicitly requested by name (`erk-diff-analysis`)
|
|
15
|
+
- Invoked by commands like `/erk:git-pr-push`
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
Load `references/commit-message-prompt.md` when analyzing a diff, then apply its principles to generate output.
|
|
20
|
+
|
|
21
|
+
## Key Principles
|
|
22
|
+
|
|
23
|
+
- Be concise and strategic - focus on significant changes
|
|
24
|
+
- Use component-level descriptions - reference modules/components, not individual functions
|
|
25
|
+
- Highlight breaking changes prominently
|
|
26
|
+
- Note test coverage patterns
|
|
27
|
+
- Use relative paths from repository root
|