@mytechtoday/augment-extensions 0.2.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +614 -39
- package/augment-extensions/coding-standards/bash/README.md +196 -0
- package/augment-extensions/coding-standards/bash/module.json +163 -0
- package/augment-extensions/coding-standards/bash/rules/naming-conventions.md +336 -0
- package/augment-extensions/coding-standards/bash/rules/universal-standards.md +289 -0
- package/augment-extensions/coding-standards/css/README.md +40 -0
- package/augment-extensions/coding-standards/css/examples/css-examples.css +550 -0
- package/augment-extensions/coding-standards/css/module.json +44 -0
- package/augment-extensions/coding-standards/css/rules/css-modern-features.md +448 -0
- package/augment-extensions/coding-standards/css/rules/css-standards.md +492 -0
- package/augment-extensions/coding-standards/html/README.md +40 -0
- package/augment-extensions/coding-standards/html/examples/html-examples.html +267 -0
- package/augment-extensions/coding-standards/html/examples/responsive-layout.html +505 -0
- package/augment-extensions/coding-standards/html/module.json +44 -0
- package/augment-extensions/coding-standards/html/rules/html-standards.md +349 -0
- package/augment-extensions/coding-standards/html-css-js/README.md +194 -0
- package/augment-extensions/coding-standards/html-css-js/examples/async-examples.js +487 -0
- package/augment-extensions/coding-standards/html-css-js/examples/css-examples.css +550 -0
- package/augment-extensions/coding-standards/html-css-js/examples/dom-examples.js +667 -0
- package/augment-extensions/coding-standards/html-css-js/examples/html-examples.html +267 -0
- package/augment-extensions/coding-standards/html-css-js/examples/javascript-examples.js +612 -0
- package/augment-extensions/coding-standards/html-css-js/examples/responsive-layout.html +505 -0
- package/augment-extensions/coding-standards/html-css-js/module.json +48 -0
- package/augment-extensions/coding-standards/html-css-js/rules/async-patterns.md +515 -0
- package/augment-extensions/coding-standards/html-css-js/rules/css-modern-features.md +448 -0
- package/augment-extensions/coding-standards/html-css-js/rules/css-standards.md +492 -0
- package/augment-extensions/coding-standards/html-css-js/rules/dom-manipulation.md +439 -0
- package/augment-extensions/coding-standards/html-css-js/rules/html-standards.md +349 -0
- package/augment-extensions/coding-standards/html-css-js/rules/javascript-standards.md +486 -0
- package/augment-extensions/coding-standards/html-css-js/rules/performance.md +463 -0
- package/augment-extensions/coding-standards/html-css-js/rules/tooling.md +543 -0
- package/augment-extensions/coding-standards/js/README.md +46 -0
- package/augment-extensions/coding-standards/js/examples/async-examples.js +487 -0
- package/augment-extensions/coding-standards/js/examples/dom-examples.js +667 -0
- package/augment-extensions/coding-standards/js/examples/javascript-examples.js +612 -0
- package/augment-extensions/coding-standards/js/module.json +49 -0
- package/augment-extensions/coding-standards/js/rules/async-patterns.md +515 -0
- package/augment-extensions/coding-standards/js/rules/dom-manipulation.md +439 -0
- package/augment-extensions/coding-standards/js/rules/javascript-standards.md +486 -0
- package/augment-extensions/coding-standards/js/rules/performance.md +463 -0
- package/augment-extensions/coding-standards/js/rules/tooling.md +543 -0
- package/augment-extensions/coding-standards/php/README.md +248 -0
- package/augment-extensions/coding-standards/php/examples/api-endpoint-example.php +204 -0
- package/augment-extensions/coding-standards/php/examples/cli-command-example.php +206 -0
- package/augment-extensions/coding-standards/php/examples/legacy-refactoring-example.php +234 -0
- package/augment-extensions/coding-standards/php/examples/web-application-example.php +211 -0
- package/augment-extensions/coding-standards/php/examples/woocommerce-extension-example.php +215 -0
- package/augment-extensions/coding-standards/php/examples/wordpress-plugin-example.php +189 -0
- package/augment-extensions/coding-standards/php/module.json +166 -0
- package/augment-extensions/coding-standards/php/rules/api-development.md +480 -0
- package/augment-extensions/coding-standards/php/rules/category-configuration.md +332 -0
- package/augment-extensions/coding-standards/php/rules/cli-tools.md +472 -0
- package/augment-extensions/coding-standards/php/rules/cms-integration.md +561 -0
- package/augment-extensions/coding-standards/php/rules/code-quality.md +402 -0
- package/augment-extensions/coding-standards/php/rules/documentation.md +425 -0
- package/augment-extensions/coding-standards/php/rules/ecommerce.md +627 -0
- package/augment-extensions/coding-standards/php/rules/error-handling.md +336 -0
- package/augment-extensions/coding-standards/php/rules/legacy-migration.md +677 -0
- package/augment-extensions/coding-standards/php/rules/naming-conventions.md +279 -0
- package/augment-extensions/coding-standards/php/rules/performance.md +392 -0
- package/augment-extensions/coding-standards/php/rules/psr-standards.md +186 -0
- package/augment-extensions/coding-standards/php/rules/security.md +358 -0
- package/augment-extensions/coding-standards/php/rules/testing.md +403 -0
- package/augment-extensions/coding-standards/php/rules/type-declarations.md +331 -0
- package/augment-extensions/coding-standards/php/rules/web-applications.md +426 -0
- package/augment-extensions/coding-standards/powershell/README.md +154 -0
- package/augment-extensions/coding-standards/powershell/examples/admin-example.ps1 +272 -0
- package/augment-extensions/coding-standards/powershell/examples/automation-example.ps1 +173 -0
- package/augment-extensions/coding-standards/powershell/examples/cloud-example.ps1 +243 -0
- package/augment-extensions/coding-standards/powershell/examples/cross-platform-example.ps1 +297 -0
- package/augment-extensions/coding-standards/powershell/examples/dsc-example.ps1 +224 -0
- package/augment-extensions/coding-standards/powershell/examples/legacy-migration-example.ps1 +340 -0
- package/augment-extensions/coding-standards/powershell/examples/module-example.psm1 +255 -0
- package/augment-extensions/coding-standards/powershell/module.json +165 -0
- package/augment-extensions/coding-standards/powershell/rules/administrative-tools.md +439 -0
- package/augment-extensions/coding-standards/powershell/rules/automation-scripts.md +240 -0
- package/augment-extensions/coding-standards/powershell/rules/cloud-orchestration.md +384 -0
- package/augment-extensions/coding-standards/powershell/rules/configuration-schema.md +383 -0
- package/augment-extensions/coding-standards/powershell/rules/cross-platform-scripts.md +482 -0
- package/augment-extensions/coding-standards/powershell/rules/dsc-configurations.md +296 -0
- package/augment-extensions/coding-standards/powershell/rules/error-handling.md +314 -0
- package/augment-extensions/coding-standards/powershell/rules/legacy-migrations.md +466 -0
- package/augment-extensions/coding-standards/powershell/rules/modules-functions.md +244 -0
- package/augment-extensions/coding-standards/powershell/rules/naming-conventions.md +266 -0
- package/augment-extensions/coding-standards/powershell/rules/performance-optimization.md +209 -0
- package/augment-extensions/coding-standards/powershell/rules/security-practices.md +314 -0
- package/augment-extensions/coding-standards/powershell/rules/testing-guidelines.md +268 -0
- package/augment-extensions/coding-standards/powershell/rules/universal-standards.md +197 -0
- package/augment-extensions/coding-standards/python/README.md +12 -8
- package/augment-extensions/coding-standards/python/examples/best-practices.py +373 -0
- package/augment-extensions/coding-standards/python/module.json +8 -4
- package/augment-extensions/coding-standards/python/rules/async-patterns.md +884 -0
- package/augment-extensions/coding-standards/python/rules/documentation.md +831 -0
- package/augment-extensions/coding-standards/python/rules/error-handling.md +855 -68
- package/augment-extensions/coding-standards/python/rules/testing.md +409 -0
- package/augment-extensions/coding-standards/python/rules/tooling.md +446 -0
- package/augment-extensions/coding-standards/python/rules/type-hints.md +115 -50
- package/augment-extensions/collections/html-css-js/README.md +82 -0
- package/augment-extensions/collections/html-css-js/collection.json +41 -0
- package/augment-extensions/domain-rules/database/README.md +161 -0
- package/augment-extensions/domain-rules/database/examples/flat-database-example.md +793 -0
- package/augment-extensions/domain-rules/database/examples/hybrid-database-example.md +1132 -0
- package/augment-extensions/domain-rules/database/examples/nosql-document-example.md +868 -0
- package/augment-extensions/domain-rules/database/examples/nosql-graph-example.md +805 -0
- package/augment-extensions/domain-rules/database/examples/relational-schema-example.md +621 -0
- package/augment-extensions/domain-rules/database/examples/vector-database-example.md +965 -0
- package/augment-extensions/domain-rules/database/module.json +28 -0
- package/augment-extensions/domain-rules/database/rules/flat-databases.md +624 -0
- package/augment-extensions/domain-rules/database/rules/nosql-databases.md +588 -0
- package/augment-extensions/domain-rules/database/rules/nosql-document-stores.md +856 -0
- package/augment-extensions/domain-rules/database/rules/nosql-graph-databases.md +778 -0
- package/augment-extensions/domain-rules/database/rules/nosql-key-value-stores.md +963 -0
- package/augment-extensions/domain-rules/database/rules/performance-optimization.md +1076 -0
- package/augment-extensions/domain-rules/database/rules/relational-databases.md +697 -0
- package/augment-extensions/domain-rules/database/rules/relational-indexing.md +671 -0
- package/augment-extensions/domain-rules/database/rules/relational-query-optimization.md +607 -0
- package/augment-extensions/domain-rules/database/rules/relational-schema-design.md +907 -0
- package/augment-extensions/domain-rules/database/rules/relational-transactions.md +783 -0
- package/augment-extensions/domain-rules/database/rules/security-standards.md +980 -0
- package/augment-extensions/domain-rules/database/rules/universal-best-practices.md +485 -0
- package/augment-extensions/domain-rules/database/rules/vector-databases.md +521 -0
- package/augment-extensions/domain-rules/database/rules/vector-embeddings.md +858 -0
- package/augment-extensions/domain-rules/database/rules/vector-indexing.md +934 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-latte/README.md +23 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-latte/module.json +26 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-mocha/README.md +23 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-mocha/module.json +26 -0
- package/augment-extensions/domain-rules/design/color/themes/dracula/README.md +23 -0
- package/augment-extensions/domain-rules/design/color/themes/dracula/module.json +26 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-dark/README.md +23 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-dark/module.json +26 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-light/README.md +23 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-light/module.json +26 -0
- package/augment-extensions/domain-rules/design/color/themes/high-contrast/README.md +27 -0
- package/augment-extensions/domain-rules/design/color/themes/high-contrast/module.json +26 -0
- package/augment-extensions/domain-rules/design/color/themes/monokai/README.md +23 -0
- package/augment-extensions/domain-rules/design/color/themes/monokai/module.json +26 -0
- package/augment-extensions/domain-rules/design/color/themes/nord/README.md +23 -0
- package/augment-extensions/domain-rules/design/color/themes/nord/module.json +26 -0
- package/augment-extensions/domain-rules/design/color/themes/one-dark/README.md +23 -0
- package/augment-extensions/domain-rules/design/color/themes/one-dark/module.json +26 -0
- package/augment-extensions/domain-rules/design/color/themes/one-light/README.md +23 -0
- package/augment-extensions/domain-rules/design/color/themes/one-light/module.json +26 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-dark/README.md +23 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-dark/module.json +26 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-light/README.md +23 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-light/module.json +26 -0
- package/augment-extensions/domain-rules/design/color/themes/tokyo-night/README.md +23 -0
- package/augment-extensions/domain-rules/design/color/themes/tokyo-night/module.json +26 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/README.md +136 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/SCHEMA-VALIDATION-REPORT.md +216 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/examples/brand-kit-example.yaml +292 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/examples/campaign-brief-example.yaml +389 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/examples/content-calendar-example.yaml +643 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/examples/email-newsletter-example.md +376 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/examples/landing-page-example.md +934 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/examples/ppc-ad-copy-example.md +301 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/examples/seo-blog-post-example.md +347 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/examples/social-media-campaign-example.md +606 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/module.json +50 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/rules/affiliate-influencer-marketing.md +593 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/rules/asset-management.md +418 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/rules/brand-consistency.md +210 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/rules/content-marketing.md +337 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/rules/conversion-optimization.md +455 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/rules/direct-sales.md +499 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/rules/email-marketing.md +439 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/rules/legal-compliance.md +227 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/rules/ppc-advertising.md +569 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/rules/seo-optimization.md +470 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/rules/social-media-marketing.md +414 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/rules/universal-marketing.md +177 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/schemas/asset-inventory.schema.json +247 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/schemas/brand-kit.schema.json +326 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/schemas/campaign-brief.schema.json +342 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/schemas/color-palette.schema.json +223 -0
- package/augment-extensions/domain-rules/marketing-standards/seo-sales-marketing/schemas/content-template.schema.json +383 -0
- package/augment-extensions/domain-rules/mcp/README.md +150 -0
- package/augment-extensions/domain-rules/mcp/examples/compressed-example.md +522 -0
- package/augment-extensions/domain-rules/mcp/examples/graph-augmented-example.md +520 -0
- package/augment-extensions/domain-rules/mcp/examples/hybrid-example.md +570 -0
- package/augment-extensions/domain-rules/mcp/examples/state-based-example.md +427 -0
- package/augment-extensions/domain-rules/mcp/examples/token-based-example.md +435 -0
- package/augment-extensions/domain-rules/mcp/examples/vector-based-example.md +502 -0
- package/augment-extensions/domain-rules/mcp/module.json +49 -0
- package/augment-extensions/domain-rules/mcp/rules/compressed-mcp.md +595 -0
- package/augment-extensions/domain-rules/mcp/rules/configuration.md +345 -0
- package/augment-extensions/domain-rules/mcp/rules/graph-augmented-mcp.md +687 -0
- package/augment-extensions/domain-rules/mcp/rules/hybrid-mcp.md +636 -0
- package/augment-extensions/domain-rules/mcp/rules/state-based-mcp.md +484 -0
- package/augment-extensions/domain-rules/mcp/rules/testing-validation.md +360 -0
- package/augment-extensions/domain-rules/mcp/rules/token-based-mcp.md +393 -0
- package/augment-extensions/domain-rules/mcp/rules/universal-rules.md +194 -0
- package/augment-extensions/domain-rules/mcp/rules/vector-based-mcp.md +625 -0
- package/augment-extensions/workflows/beads/module.json +4 -3
- package/augment-extensions/workflows/beads-integration/IMPLEMENTATION-STATUS.md +145 -0
- package/augment-extensions/workflows/beads-integration/README.md +143 -0
- package/augment-extensions/workflows/beads-integration/config/defaults.json +32 -0
- package/augment-extensions/workflows/beads-integration/config/schema.json +140 -0
- package/augment-extensions/workflows/beads-integration/examples/basic-task-generation.md +293 -0
- package/augment-extensions/workflows/beads-integration/module.json +75 -0
- package/augment-extensions/workflows/beads-integration/rules/core-rules.md +219 -0
- package/augment-extensions/workflows/beads-integration/rules/effectiveness-standards.md +256 -0
- package/augment-extensions/workflows/beads-integration/rules/task-generation.md +607 -0
- package/augment-extensions/workflows/database/README.md +195 -0
- package/augment-extensions/workflows/database/ai-prompt-testing.md +295 -0
- package/augment-extensions/workflows/database/examples/migration-example.md +498 -0
- package/augment-extensions/workflows/database/examples/optimization-example.md +496 -0
- package/augment-extensions/workflows/database/examples/schema-design-example.md +444 -0
- package/augment-extensions/workflows/database/module.json +42 -0
- package/augment-extensions/workflows/database/rules/data-migration.md +249 -0
- package/augment-extensions/workflows/database/rules/documentation-standards.md +339 -0
- package/augment-extensions/workflows/database/rules/migration-workflow.md +352 -0
- package/augment-extensions/workflows/database/rules/optimization-workflow.md +435 -0
- package/augment-extensions/workflows/database/rules/schema-design-workflow.md +535 -0
- package/augment-extensions/workflows/database/rules/testing-patterns.md +305 -0
- package/augment-extensions/workflows/database/rules/workflow.md +458 -0
- package/augment-extensions/workflows/openspec/module.json +4 -3
- package/augment-extensions/writing-standards/screenplay/README.md +300 -0
- package/augment-extensions/writing-standards/screenplay/_templates/README.md +121 -0
- package/augment-extensions/writing-standards/screenplay/_templates/genre-template.md +153 -0
- package/augment-extensions/writing-standards/screenplay/_templates/style-template.md +243 -0
- package/augment-extensions/writing-standards/screenplay/_templates/theme-template.md +213 -0
- package/augment-extensions/writing-standards/screenplay/examples/aaa-hollywood-scene.fountain +164 -0
- package/augment-extensions/writing-standards/screenplay/examples/beat-sheet-example.yaml +95 -0
- package/augment-extensions/writing-standards/screenplay/examples/character-profile-example.yaml +116 -0
- package/augment-extensions/writing-standards/screenplay/examples/commercial-30sec.fountain +151 -0
- package/augment-extensions/writing-standards/screenplay/examples/independent-monologue.fountain +67 -0
- package/augment-extensions/writing-standards/screenplay/examples/news-segment.fountain +142 -0
- package/augment-extensions/writing-standards/screenplay/examples/plot-outline-example.yaml +184 -0
- package/augment-extensions/writing-standards/screenplay/examples/tv-episode-teaser.fountain +204 -0
- package/augment-extensions/writing-standards/screenplay/genres/README.md +181 -0
- package/augment-extensions/writing-standards/screenplay/genres/examples/.gitkeep +2 -0
- package/augment-extensions/writing-standards/screenplay/genres/module.json +70 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/.gitkeep +2 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/action.md +399 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/adventure.md +407 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/animation.md +293 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/biographical.md +293 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/comedy.md +401 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/documentary.md +293 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/drama.md +409 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/fantasy.md +293 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/historical.md +293 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/horror.md +268 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/musical.md +294 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/mystery.md +293 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/noir.md +294 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/romance.md +293 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/sci-fi.md +289 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/superhero.md +293 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/thriller.md +294 -0
- package/augment-extensions/writing-standards/screenplay/genres/rules/western.md +293 -0
- package/augment-extensions/writing-standards/screenplay/module.json +124 -0
- package/augment-extensions/writing-standards/screenplay/rules/aaa-hollywood-films.md +339 -0
- package/augment-extensions/writing-standards/screenplay/rules/ai-integration-testing.md +329 -0
- package/augment-extensions/writing-standards/screenplay/rules/character-development.md +169 -0
- package/augment-extensions/writing-standards/screenplay/rules/commercials.md +437 -0
- package/augment-extensions/writing-standards/screenplay/rules/dialogue-writing.md +263 -0
- package/augment-extensions/writing-standards/screenplay/rules/diversity-inclusion.md +261 -0
- package/augment-extensions/writing-standards/screenplay/rules/examples-guide.md +315 -0
- package/augment-extensions/writing-standards/screenplay/rules/formatting-validation.md +413 -0
- package/augment-extensions/writing-standards/screenplay/rules/fountain-format.md +372 -0
- package/augment-extensions/writing-standards/screenplay/rules/independent-films.md +374 -0
- package/augment-extensions/writing-standards/screenplay/rules/live-tv-productions.md +443 -0
- package/augment-extensions/writing-standards/screenplay/rules/narrative-structures.md +207 -0
- package/augment-extensions/writing-standards/screenplay/rules/news-broadcasts.md +444 -0
- package/augment-extensions/writing-standards/screenplay/rules/pacing-timing.md +331 -0
- package/augment-extensions/writing-standards/screenplay/rules/quality-review-checklist.md +334 -0
- package/augment-extensions/writing-standards/screenplay/rules/quick-reference.md +299 -0
- package/augment-extensions/writing-standards/screenplay/rules/screen-continuity.md +263 -0
- package/augment-extensions/writing-standards/screenplay/rules/streaming-content.md +412 -0
- package/augment-extensions/writing-standards/screenplay/rules/trope-management.md +370 -0
- package/augment-extensions/writing-standards/screenplay/rules/tv-series.md +374 -0
- package/augment-extensions/writing-standards/screenplay/rules/universal-formatting.md +339 -0
- package/augment-extensions/writing-standards/screenplay/rules/vscode-integration.md +277 -0
- package/augment-extensions/writing-standards/screenplay/rules/web-content.md +393 -0
- package/augment-extensions/writing-standards/screenplay/schemas/beat-sheet.json +332 -0
- package/augment-extensions/writing-standards/screenplay/schemas/character-profile.json +247 -0
- package/augment-extensions/writing-standards/screenplay/schemas/feature-selection.json +200 -0
- package/augment-extensions/writing-standards/screenplay/schemas/plot-outline.json +233 -0
- package/augment-extensions/writing-standards/screenplay/schemas/screenplay-config.json +245 -0
- package/augment-extensions/writing-standards/screenplay/schemas/trope-inventory.json +221 -0
- package/augment-extensions/writing-standards/screenplay/styles/README.md +159 -0
- package/augment-extensions/writing-standards/screenplay/styles/examples/.gitkeep +2 -0
- package/augment-extensions/writing-standards/screenplay/styles/examples/style-applications.md +1449 -0
- package/augment-extensions/writing-standards/screenplay/styles/module.json +64 -0
- package/augment-extensions/writing-standards/screenplay/styles/rules/.gitkeep +2 -0
- package/augment-extensions/writing-standards/screenplay/styles/rules/dialogue-centric.md +520 -0
- package/augment-extensions/writing-standards/screenplay/styles/rules/ensemble.md +499 -0
- package/augment-extensions/writing-standards/screenplay/styles/rules/epic.md +497 -0
- package/augment-extensions/writing-standards/screenplay/styles/rules/experimental.md +492 -0
- package/augment-extensions/writing-standards/screenplay/styles/rules/flashback.md +509 -0
- package/augment-extensions/writing-standards/screenplay/styles/rules/linear.md +490 -0
- package/augment-extensions/writing-standards/screenplay/styles/rules/minimalist.md +499 -0
- package/augment-extensions/writing-standards/screenplay/styles/rules/non-linear.md +501 -0
- package/augment-extensions/writing-standards/screenplay/styles/rules/poetic.md +499 -0
- package/augment-extensions/writing-standards/screenplay/styles/rules/realistic.md +498 -0
- package/augment-extensions/writing-standards/screenplay/styles/rules/satirical.md +499 -0
- package/augment-extensions/writing-standards/screenplay/styles/rules/surreal.md +508 -0
- package/augment-extensions/writing-standards/screenplay/styles/rules/voice-over.md +500 -0
- package/augment-extensions/writing-standards/screenplay/themes/README.md +158 -0
- package/augment-extensions/writing-standards/screenplay/themes/examples/.gitkeep +2 -0
- package/augment-extensions/writing-standards/screenplay/themes/examples/common-mistakes-and-fixes.md +643 -0
- package/augment-extensions/writing-standards/screenplay/themes/examples/complete-scene-example.md +311 -0
- package/augment-extensions/writing-standards/screenplay/themes/examples/individual-theme-examples.md +562 -0
- package/augment-extensions/writing-standards/screenplay/themes/examples/multi-theme-weaving.md +538 -0
- package/augment-extensions/writing-standards/screenplay/themes/examples/theme-application-guide.md +432 -0
- package/augment-extensions/writing-standards/screenplay/themes/examples/theme-integration-across-acts.md +637 -0
- package/augment-extensions/writing-standards/screenplay/themes/module.json +66 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/.gitkeep +2 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/ambition.md +458 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/betrayal.md +490 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/environment.md +458 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/fate.md +459 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/friendship.md +491 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/growth.md +491 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/identity.md +490 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/isolation.md +464 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/justice.md +461 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/love.md +489 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/power.md +494 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/redemption.md +483 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/revenge.md +489 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/survival.md +496 -0
- package/augment-extensions/writing-standards/screenplay/themes/rules/technology.md +463 -0
- package/cli/MODULES.md +302 -0
- package/cli/dist/cli.js +168 -10
- package/cli/dist/cli.js.map +1 -1
- package/cli/dist/commands/catalog.d.ts +13 -0
- package/cli/dist/commands/catalog.d.ts.map +1 -0
- package/cli/dist/commands/catalog.js +104 -0
- package/cli/dist/commands/catalog.js.map +1 -0
- package/cli/dist/commands/gui.d.ts +6 -0
- package/cli/dist/commands/gui.d.ts.map +1 -0
- package/cli/dist/commands/gui.js +211 -0
- package/cli/dist/commands/gui.js.map +1 -0
- package/cli/dist/commands/init.d.ts.map +1 -1
- package/cli/dist/commands/init.js +12 -0
- package/cli/dist/commands/init.js.map +1 -1
- package/cli/dist/commands/install-rules.d.ts +14 -0
- package/cli/dist/commands/install-rules.d.ts.map +1 -0
- package/cli/dist/commands/install-rules.js +127 -0
- package/cli/dist/commands/install-rules.js.map +1 -0
- package/cli/dist/commands/link.d.ts.map +1 -1
- package/cli/dist/commands/link.js +9 -11
- package/cli/dist/commands/link.js.map +1 -1
- package/cli/dist/commands/list.d.ts.map +1 -1
- package/cli/dist/commands/list.js +11 -28
- package/cli/dist/commands/list.js.map +1 -1
- package/cli/dist/commands/mcp.d.ts +48 -0
- package/cli/dist/commands/mcp.d.ts.map +1 -0
- package/cli/dist/commands/mcp.js +229 -0
- package/cli/dist/commands/mcp.js.map +1 -0
- package/cli/dist/commands/self-remove.d.ts +7 -0
- package/cli/dist/commands/self-remove.d.ts.map +1 -0
- package/cli/dist/commands/self-remove.js +179 -0
- package/cli/dist/commands/self-remove.js.map +1 -0
- package/cli/dist/commands/show.d.ts +19 -0
- package/cli/dist/commands/show.d.ts.map +1 -1
- package/cli/dist/commands/show.js +478 -63
- package/cli/dist/commands/show.js.map +1 -1
- package/cli/dist/commands/skill.d.ts +67 -0
- package/cli/dist/commands/skill.d.ts.map +1 -0
- package/cli/dist/commands/skill.js +513 -0
- package/cli/dist/commands/skill.js.map +1 -0
- package/cli/dist/commands/unlink.d.ts +6 -0
- package/cli/dist/commands/unlink.d.ts.map +1 -0
- package/cli/dist/commands/unlink.js +115 -0
- package/cli/dist/commands/unlink.js.map +1 -0
- package/cli/dist/commands/validate.d.ts +6 -0
- package/cli/dist/commands/validate.d.ts.map +1 -0
- package/cli/dist/commands/validate.js +159 -0
- package/cli/dist/commands/validate.js.map +1 -0
- package/cli/dist/types/gui.d.ts +62 -0
- package/cli/dist/types/gui.d.ts.map +1 -0
- package/cli/dist/types/gui.js +30 -0
- package/cli/dist/types/gui.js.map +1 -0
- package/cli/dist/utils/catalog-sync.d.ts +22 -0
- package/cli/dist/utils/catalog-sync.d.ts.map +1 -0
- package/cli/dist/utils/catalog-sync.js +157 -0
- package/cli/dist/utils/catalog-sync.js.map +1 -0
- package/cli/dist/utils/character-count.d.ts +56 -0
- package/cli/dist/utils/character-count.d.ts.map +1 -0
- package/cli/dist/utils/character-count.js +190 -0
- package/cli/dist/utils/character-count.js.map +1 -0
- package/cli/dist/utils/documentation-validator.d.ts +18 -0
- package/cli/dist/utils/documentation-validator.d.ts.map +1 -0
- package/cli/dist/utils/documentation-validator.js +233 -0
- package/cli/dist/utils/documentation-validator.js.map +1 -0
- package/cli/dist/utils/gui-helpers.d.ts +23 -0
- package/cli/dist/utils/gui-helpers.d.ts.map +1 -0
- package/cli/dist/utils/gui-helpers.js +159 -0
- package/cli/dist/utils/gui-helpers.js.map +1 -0
- package/cli/dist/utils/install-rules.d.ts +32 -0
- package/cli/dist/utils/install-rules.d.ts.map +1 -0
- package/cli/dist/utils/install-rules.js +375 -0
- package/cli/dist/utils/install-rules.js.map +1 -0
- package/cli/dist/utils/mcp-integration.d.ts +70 -0
- package/cli/dist/utils/mcp-integration.d.ts.map +1 -0
- package/cli/dist/utils/mcp-integration.js +292 -0
- package/cli/dist/utils/mcp-integration.js.map +1 -0
- package/cli/dist/utils/module-system.d.ts +232 -0
- package/cli/dist/utils/module-system.d.ts.map +1 -0
- package/cli/dist/utils/module-system.js +900 -0
- package/cli/dist/utils/module-system.js.map +1 -0
- package/cli/dist/utils/modules-catalog.d.ts +33 -0
- package/cli/dist/utils/modules-catalog.d.ts.map +1 -0
- package/cli/dist/utils/modules-catalog.js +163 -0
- package/cli/dist/utils/modules-catalog.js.map +1 -0
- package/cli/dist/utils/rule-install-hooks.d.ts +19 -0
- package/cli/dist/utils/rule-install-hooks.d.ts.map +1 -0
- package/cli/dist/utils/rule-install-hooks.js +224 -0
- package/cli/dist/utils/rule-install-hooks.js.map +1 -0
- package/cli/dist/utils/skill-system.d.ts +95 -0
- package/cli/dist/utils/skill-system.d.ts.map +1 -0
- package/cli/dist/utils/skill-system.js +313 -0
- package/cli/dist/utils/skill-system.js.map +1 -0
- package/modules.md +559 -105
- package/package.json +17 -6
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
# Python Error Handling
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Comprehensive exception handling patterns for robust Python code, including custom exceptions, context managers, and contextlib utilities.
|
|
4
4
|
|
|
5
5
|
## Basic Exception Handling
|
|
6
6
|
|
|
7
|
+
### Specific Exceptions
|
|
8
|
+
|
|
9
|
+
Always catch specific exceptions rather than using bare `except:` clauses.
|
|
10
|
+
|
|
7
11
|
```python
|
|
8
12
|
# Good - Specific exception
|
|
9
13
|
try:
|
|
10
14
|
result = int(user_input)
|
|
11
15
|
except ValueError as e:
|
|
12
|
-
|
|
16
|
+
logger.error(f"Invalid input: {e}")
|
|
13
17
|
result = 0
|
|
14
18
|
|
|
15
19
|
# Bad - Bare except
|
|
16
20
|
try:
|
|
17
21
|
result = int(user_input)
|
|
18
|
-
except: # Don't do this
|
|
22
|
+
except: # Don't do this - catches SystemExit, KeyboardInterrupt, etc.
|
|
23
|
+
result = 0
|
|
24
|
+
|
|
25
|
+
# Bad - Too broad
|
|
26
|
+
try:
|
|
27
|
+
result = int(user_input)
|
|
28
|
+
except Exception: # Still too broad for most cases
|
|
19
29
|
result = 0
|
|
20
30
|
```
|
|
21
31
|
|
|
22
|
-
|
|
32
|
+
### Multiple Exceptions
|
|
23
33
|
|
|
24
34
|
```python
|
|
25
35
|
# Handle different exceptions differently
|
|
@@ -27,195 +37,972 @@ try:
|
|
|
27
37
|
with open(file_path) as f:
|
|
28
38
|
data = json.load(f)
|
|
29
39
|
except FileNotFoundError:
|
|
30
|
-
|
|
40
|
+
logger.warning(f"File not found: {file_path}")
|
|
31
41
|
data = {}
|
|
32
42
|
except json.JSONDecodeError as e:
|
|
33
|
-
|
|
43
|
+
logger.error(f"Invalid JSON in {file_path}: {e}")
|
|
34
44
|
data = {}
|
|
35
45
|
except PermissionError:
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
logger.error(f"Permission denied: {file_path}")
|
|
47
|
+
raise # Re-raise if we can't handle it
|
|
38
48
|
|
|
39
49
|
# Handle multiple exceptions the same way
|
|
40
50
|
try:
|
|
41
51
|
result = perform_operation()
|
|
42
52
|
except (ValueError, TypeError, KeyError) as e:
|
|
43
|
-
|
|
53
|
+
logger.error(f"Operation failed: {e}")
|
|
44
54
|
result = None
|
|
45
55
|
```
|
|
46
56
|
|
|
47
|
-
## Finally
|
|
57
|
+
## Try-Except-Else-Finally
|
|
58
|
+
|
|
59
|
+
### The Complete Pattern
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
try:
|
|
63
|
+
# Code that might raise exceptions
|
|
64
|
+
result = risky_operation()
|
|
65
|
+
except ValueError as e:
|
|
66
|
+
# Handle specific exception
|
|
67
|
+
logger.error(f"Value error: {e}")
|
|
68
|
+
result = None
|
|
69
|
+
except TypeError as e:
|
|
70
|
+
# Handle another specific exception
|
|
71
|
+
logger.error(f"Type error: {e}")
|
|
72
|
+
result = None
|
|
73
|
+
else:
|
|
74
|
+
# Runs only if no exception was raised
|
|
75
|
+
logger.info("Operation succeeded")
|
|
76
|
+
process_result(result)
|
|
77
|
+
finally:
|
|
78
|
+
# Always runs, even if exception occurred or return was called
|
|
79
|
+
cleanup_resources()
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Using Else Clause
|
|
83
|
+
|
|
84
|
+
The `else` clause runs only if no exception was raised in the `try` block.
|
|
48
85
|
|
|
49
86
|
```python
|
|
50
|
-
#
|
|
87
|
+
# Good - Separates success logic from try block
|
|
88
|
+
try:
|
|
89
|
+
data = load_data(file_path)
|
|
90
|
+
except FileNotFoundError:
|
|
91
|
+
logger.error(f"File not found: {file_path}")
|
|
92
|
+
return None
|
|
93
|
+
else:
|
|
94
|
+
# Only runs if load_data succeeded
|
|
95
|
+
validate_data(data)
|
|
96
|
+
return process_data(data)
|
|
97
|
+
|
|
98
|
+
# Less clear - success logic mixed with risky code
|
|
99
|
+
try:
|
|
100
|
+
data = load_data(file_path)
|
|
101
|
+
validate_data(data) # This is also in the try block
|
|
102
|
+
return process_data(data)
|
|
103
|
+
except FileNotFoundError:
|
|
104
|
+
logger.error(f"File not found: {file_path}")
|
|
105
|
+
return None
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Using Finally for Cleanup
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
# Manual cleanup with finally
|
|
112
|
+
file = None
|
|
51
113
|
try:
|
|
52
114
|
file = open(file_path)
|
|
53
115
|
data = file.read()
|
|
54
116
|
except FileNotFoundError:
|
|
55
|
-
|
|
117
|
+
logger.error("File not found")
|
|
56
118
|
data = None
|
|
57
119
|
finally:
|
|
58
|
-
if
|
|
120
|
+
if file is not None:
|
|
59
121
|
file.close()
|
|
60
122
|
|
|
61
|
-
#
|
|
123
|
+
# Better - Use context manager instead (see below)
|
|
62
124
|
try:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
process_result(result)
|
|
69
|
-
finally:
|
|
70
|
-
cleanup()
|
|
125
|
+
with open(file_path) as file:
|
|
126
|
+
data = file.read()
|
|
127
|
+
except FileNotFoundError:
|
|
128
|
+
logger.error("File not found")
|
|
129
|
+
data = None
|
|
71
130
|
```
|
|
72
131
|
|
|
73
132
|
## Context Managers
|
|
74
133
|
|
|
134
|
+
Context managers provide automatic resource management using the `with` statement. They ensure cleanup code runs even if exceptions occur.
|
|
135
|
+
|
|
136
|
+
### Built-in Context Managers
|
|
137
|
+
|
|
75
138
|
```python
|
|
76
|
-
#
|
|
139
|
+
# File handling - Automatic close
|
|
77
140
|
with open(file_path) as f:
|
|
78
141
|
data = f.read()
|
|
142
|
+
# File is automatically closed here, even if exception occurred
|
|
79
143
|
|
|
80
|
-
# Multiple context managers
|
|
144
|
+
# Multiple context managers (Python 3.1+)
|
|
81
145
|
with open(input_file) as f_in, open(output_file, 'w') as f_out:
|
|
82
146
|
data = f_in.read()
|
|
83
147
|
f_out.write(process(data))
|
|
84
148
|
|
|
85
|
-
#
|
|
149
|
+
# Parenthesized context managers (Python 3.10+)
|
|
150
|
+
with (
|
|
151
|
+
open(input_file) as f_in,
|
|
152
|
+
open(output_file, 'w') as f_out,
|
|
153
|
+
open(log_file, 'a') as f_log,
|
|
154
|
+
):
|
|
155
|
+
data = f_in.read()
|
|
156
|
+
f_out.write(process(data))
|
|
157
|
+
f_log.write(f"Processed {input_file}\n")
|
|
158
|
+
|
|
159
|
+
# Threading locks
|
|
160
|
+
import threading
|
|
161
|
+
|
|
162
|
+
lock = threading.Lock()
|
|
163
|
+
with lock:
|
|
164
|
+
# Critical section - lock is automatically released
|
|
165
|
+
shared_resource.modify()
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Custom Context Managers - Class-Based
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from typing import Optional
|
|
172
|
+
|
|
173
|
+
class DatabaseConnection:
|
|
174
|
+
"""Context manager for database connections."""
|
|
175
|
+
|
|
176
|
+
def __init__(self, db_url: str):
|
|
177
|
+
self.db_url = db_url
|
|
178
|
+
self.conn: Optional[Connection] = None
|
|
179
|
+
|
|
180
|
+
def __enter__(self) -> Connection:
|
|
181
|
+
"""Establish connection when entering context."""
|
|
182
|
+
self.conn = connect(self.db_url)
|
|
183
|
+
logger.info(f"Connected to {self.db_url}")
|
|
184
|
+
return self.conn
|
|
185
|
+
|
|
186
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> bool:
|
|
187
|
+
"""Close connection when exiting context.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
exc_type: Exception type if exception occurred, None otherwise
|
|
191
|
+
exc_val: Exception value if exception occurred, None otherwise
|
|
192
|
+
exc_tb: Exception traceback if exception occurred, None otherwise
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
False to propagate exceptions, True to suppress them
|
|
196
|
+
"""
|
|
197
|
+
if self.conn is not None:
|
|
198
|
+
self.conn.close()
|
|
199
|
+
logger.info(f"Closed connection to {self.db_url}")
|
|
200
|
+
|
|
201
|
+
# Log exception if one occurred
|
|
202
|
+
if exc_type is not None:
|
|
203
|
+
logger.error(f"Exception in database context: {exc_val}")
|
|
204
|
+
|
|
205
|
+
# Return False to propagate exception, True to suppress it
|
|
206
|
+
return False
|
|
207
|
+
|
|
208
|
+
# Usage
|
|
209
|
+
with DatabaseConnection("postgresql://localhost/mydb") as conn:
|
|
210
|
+
conn.execute("SELECT * FROM users")
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Custom Context Managers - contextlib.contextmanager
|
|
214
|
+
|
|
215
|
+
The `@contextmanager` decorator provides a simpler way to create context managers using generators.
|
|
216
|
+
|
|
217
|
+
```python
|
|
86
218
|
from contextlib import contextmanager
|
|
219
|
+
from typing import Generator, Optional
|
|
220
|
+
import time
|
|
87
221
|
|
|
88
222
|
@contextmanager
|
|
89
|
-
def database_connection(db_url: str):
|
|
223
|
+
def database_connection(db_url: str) -> Generator[Connection, None, None]:
|
|
224
|
+
"""Context manager for database connections.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
db_url: Database connection URL
|
|
228
|
+
|
|
229
|
+
Yields:
|
|
230
|
+
Active database connection
|
|
231
|
+
|
|
232
|
+
Example:
|
|
233
|
+
with database_connection("postgresql://...") as conn:
|
|
234
|
+
conn.execute("SELECT * FROM users")
|
|
235
|
+
"""
|
|
90
236
|
conn = connect(db_url)
|
|
91
237
|
try:
|
|
238
|
+
logger.info(f"Connected to {db_url}")
|
|
92
239
|
yield conn
|
|
240
|
+
except Exception as e:
|
|
241
|
+
logger.error(f"Database error: {e}")
|
|
242
|
+
raise
|
|
93
243
|
finally:
|
|
94
244
|
conn.close()
|
|
245
|
+
logger.info(f"Closed connection to {db_url}")
|
|
246
|
+
|
|
247
|
+
@contextmanager
|
|
248
|
+
def timer(name: str) -> Generator[None, None, None]:
|
|
249
|
+
"""Context manager to time code execution.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
name: Name of the timed operation
|
|
253
|
+
|
|
254
|
+
Example:
|
|
255
|
+
with timer("data processing"):
|
|
256
|
+
process_large_dataset()
|
|
257
|
+
"""
|
|
258
|
+
start = time.time()
|
|
259
|
+
try:
|
|
260
|
+
yield
|
|
261
|
+
finally:
|
|
262
|
+
elapsed = time.time() - start
|
|
263
|
+
logger.info(f"{name} took {elapsed:.2f} seconds")
|
|
264
|
+
|
|
265
|
+
@contextmanager
|
|
266
|
+
def temporary_directory() -> Generator[Path, None, None]:
|
|
267
|
+
"""Context manager for temporary directory.
|
|
268
|
+
|
|
269
|
+
Yields:
|
|
270
|
+
Path to temporary directory
|
|
271
|
+
|
|
272
|
+
Example:
|
|
273
|
+
with temporary_directory() as tmpdir:
|
|
274
|
+
(tmpdir / "file.txt").write_text("data")
|
|
275
|
+
# Directory is automatically deleted here
|
|
276
|
+
"""
|
|
277
|
+
import tempfile
|
|
278
|
+
import shutil
|
|
279
|
+
|
|
280
|
+
tmpdir = Path(tempfile.mkdtemp())
|
|
281
|
+
try:
|
|
282
|
+
yield tmpdir
|
|
283
|
+
finally:
|
|
284
|
+
shutil.rmtree(tmpdir, ignore_errors=True)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### contextlib Utilities
|
|
288
|
+
|
|
289
|
+
```python
|
|
290
|
+
from contextlib import (
|
|
291
|
+
contextmanager,
|
|
292
|
+
suppress,
|
|
293
|
+
redirect_stdout,
|
|
294
|
+
redirect_stderr,
|
|
295
|
+
ExitStack,
|
|
296
|
+
nullcontext,
|
|
297
|
+
)
|
|
298
|
+
import io
|
|
299
|
+
|
|
300
|
+
# suppress - Ignore specific exceptions
|
|
301
|
+
from contextlib import suppress
|
|
302
|
+
|
|
303
|
+
# Instead of try-except
|
|
304
|
+
try:
|
|
305
|
+
os.remove(file_path)
|
|
306
|
+
except FileNotFoundError:
|
|
307
|
+
pass
|
|
308
|
+
|
|
309
|
+
# Use suppress
|
|
310
|
+
with suppress(FileNotFoundError):
|
|
311
|
+
os.remove(file_path)
|
|
312
|
+
|
|
313
|
+
# Suppress multiple exceptions
|
|
314
|
+
with suppress(FileNotFoundError, PermissionError):
|
|
315
|
+
os.remove(file_path)
|
|
316
|
+
|
|
317
|
+
# redirect_stdout/redirect_stderr - Redirect output
|
|
318
|
+
output = io.StringIO()
|
|
319
|
+
with redirect_stdout(output):
|
|
320
|
+
print("This goes to output variable")
|
|
321
|
+
print("Not to console")
|
|
322
|
+
|
|
323
|
+
captured = output.getvalue() # "This goes to output variable\nNot to console\n"
|
|
324
|
+
|
|
325
|
+
# ExitStack - Manage dynamic number of context managers
|
|
326
|
+
from contextlib import ExitStack
|
|
327
|
+
|
|
328
|
+
def process_files(file_paths: list[str]) -> None:
|
|
329
|
+
"""Process multiple files with dynamic context managers."""
|
|
330
|
+
with ExitStack() as stack:
|
|
331
|
+
# Open all files and register them with the stack
|
|
332
|
+
files = [stack.enter_context(open(path)) for path in file_paths]
|
|
333
|
+
|
|
334
|
+
# Process all files
|
|
335
|
+
for f in files:
|
|
336
|
+
process_file(f)
|
|
337
|
+
|
|
338
|
+
# All files automatically closed when exiting
|
|
339
|
+
|
|
340
|
+
# ExitStack with callbacks
|
|
341
|
+
with ExitStack() as stack:
|
|
342
|
+
# Register cleanup callbacks
|
|
343
|
+
stack.callback(cleanup_temp_files)
|
|
344
|
+
stack.callback(logger.info, "Processing complete")
|
|
345
|
+
|
|
346
|
+
# Do work
|
|
347
|
+
process_data()
|
|
348
|
+
|
|
349
|
+
# Callbacks run in LIFO order when exiting
|
|
350
|
+
|
|
351
|
+
# nullcontext - Conditional context manager (Python 3.7+)
|
|
352
|
+
from contextlib import nullcontext
|
|
353
|
+
|
|
354
|
+
def process_data(use_lock: bool = False) -> None:
|
|
355
|
+
"""Process data with optional locking."""
|
|
356
|
+
lock = threading.Lock() if use_lock else nullcontext()
|
|
357
|
+
|
|
358
|
+
with lock:
|
|
359
|
+
# This works whether lock is a real Lock or nullcontext
|
|
360
|
+
modify_shared_resource()
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Async Context Managers
|
|
364
|
+
|
|
365
|
+
```python
|
|
366
|
+
from contextlib import asynccontextmanager
|
|
367
|
+
from typing import AsyncGenerator
|
|
368
|
+
|
|
369
|
+
class AsyncDatabaseConnection:
|
|
370
|
+
"""Async context manager for database connections."""
|
|
371
|
+
|
|
372
|
+
async def __aenter__(self) -> AsyncConnection:
|
|
373
|
+
self.conn = await async_connect(self.db_url)
|
|
374
|
+
return self.conn
|
|
375
|
+
|
|
376
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb) -> bool:
|
|
377
|
+
await self.conn.close()
|
|
378
|
+
return False
|
|
379
|
+
|
|
380
|
+
# Using @asynccontextmanager decorator
|
|
381
|
+
@asynccontextmanager
|
|
382
|
+
async def async_database_connection(
|
|
383
|
+
db_url: str
|
|
384
|
+
) -> AsyncGenerator[AsyncConnection, None]:
|
|
385
|
+
"""Async context manager for database connections."""
|
|
386
|
+
conn = await async_connect(db_url)
|
|
387
|
+
try:
|
|
388
|
+
yield conn
|
|
389
|
+
finally:
|
|
390
|
+
await conn.close()
|
|
95
391
|
|
|
96
392
|
# Usage
|
|
97
|
-
|
|
98
|
-
|
|
393
|
+
async def fetch_users():
|
|
394
|
+
async with async_database_connection("postgresql://...") as conn:
|
|
395
|
+
return await conn.fetch("SELECT * FROM users")
|
|
99
396
|
```
|
|
100
397
|
|
|
101
398
|
## Custom Exceptions
|
|
102
399
|
|
|
400
|
+
Create custom exceptions for domain-specific errors. This makes error handling more precise and code more maintainable.
|
|
401
|
+
|
|
402
|
+
### Basic Custom Exceptions
|
|
403
|
+
|
|
103
404
|
```python
|
|
104
|
-
#
|
|
405
|
+
# Simple custom exception
|
|
105
406
|
class ValidationError(Exception):
|
|
106
|
-
"""Raised when validation fails"""
|
|
407
|
+
"""Raised when validation fails."""
|
|
107
408
|
pass
|
|
108
409
|
|
|
410
|
+
# Custom exception with additional attributes
|
|
109
411
|
class AuthenticationError(Exception):
|
|
110
|
-
"""Raised when authentication fails"""
|
|
412
|
+
"""Raised when authentication fails."""
|
|
413
|
+
|
|
111
414
|
def __init__(self, user_id: int, message: str = "Authentication failed"):
|
|
112
415
|
self.user_id = user_id
|
|
113
416
|
self.message = message
|
|
114
417
|
super().__init__(self.message)
|
|
115
418
|
|
|
419
|
+
def __str__(self) -> str:
|
|
420
|
+
return f"AuthenticationError(user_id={self.user_id}): {self.message}"
|
|
421
|
+
|
|
422
|
+
# Custom exception with multiple attributes
|
|
423
|
+
class DatabaseError(Exception):
|
|
424
|
+
"""Raised when database operation fails."""
|
|
425
|
+
|
|
426
|
+
def __init__(
|
|
427
|
+
self,
|
|
428
|
+
message: str,
|
|
429
|
+
query: str,
|
|
430
|
+
error_code: Optional[int] = None,
|
|
431
|
+
):
|
|
432
|
+
self.message = message
|
|
433
|
+
self.query = query
|
|
434
|
+
self.error_code = error_code
|
|
435
|
+
super().__init__(self.message)
|
|
436
|
+
|
|
116
437
|
# Usage
|
|
117
438
|
def validate_email(email: str) -> None:
|
|
439
|
+
"""Validate email format.
|
|
440
|
+
|
|
441
|
+
Args:
|
|
442
|
+
email: Email address to validate
|
|
443
|
+
|
|
444
|
+
Raises:
|
|
445
|
+
ValidationError: If email format is invalid
|
|
446
|
+
"""
|
|
118
447
|
if '@' not in email:
|
|
119
|
-
raise ValidationError(f"Invalid email: {email}")
|
|
448
|
+
raise ValidationError(f"Invalid email format: {email}")
|
|
449
|
+
|
|
450
|
+
if not email.endswith(('.com', '.org', '.net')):
|
|
451
|
+
raise ValidationError(f"Invalid email domain: {email}")
|
|
120
452
|
|
|
121
453
|
def authenticate_user(user_id: int, password: str) -> User:
|
|
454
|
+
"""Authenticate user with password.
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
user_id: User ID
|
|
458
|
+
password: User password
|
|
459
|
+
|
|
460
|
+
Returns:
|
|
461
|
+
Authenticated user object
|
|
462
|
+
|
|
463
|
+
Raises:
|
|
464
|
+
AuthenticationError: If authentication fails
|
|
465
|
+
"""
|
|
122
466
|
if not verify_password(user_id, password):
|
|
123
|
-
raise AuthenticationError(user_id)
|
|
467
|
+
raise AuthenticationError(user_id, "Invalid password")
|
|
124
468
|
return get_user(user_id)
|
|
125
469
|
```
|
|
126
470
|
|
|
471
|
+
### Exception Hierarchies
|
|
472
|
+
|
|
473
|
+
Create exception hierarchies for related errors.
|
|
474
|
+
|
|
475
|
+
```python
|
|
476
|
+
# Base exception for application
|
|
477
|
+
class AppError(Exception):
|
|
478
|
+
"""Base exception for all application errors."""
|
|
479
|
+
pass
|
|
480
|
+
|
|
481
|
+
# Database errors
|
|
482
|
+
class DatabaseError(AppError):
|
|
483
|
+
"""Base exception for database-related errors."""
|
|
484
|
+
pass
|
|
485
|
+
|
|
486
|
+
class ConnectionError(DatabaseError):
|
|
487
|
+
"""Database connection errors."""
|
|
488
|
+
pass
|
|
489
|
+
|
|
490
|
+
class QueryError(DatabaseError):
|
|
491
|
+
"""Database query errors."""
|
|
492
|
+
|
|
493
|
+
def __init__(self, message: str, query: str):
|
|
494
|
+
self.query = query
|
|
495
|
+
super().__init__(f"{message}: {query}")
|
|
496
|
+
|
|
497
|
+
class TransactionError(DatabaseError):
|
|
498
|
+
"""Database transaction errors."""
|
|
499
|
+
pass
|
|
500
|
+
|
|
501
|
+
# API errors
|
|
502
|
+
class APIError(AppError):
|
|
503
|
+
"""Base exception for API-related errors."""
|
|
504
|
+
|
|
505
|
+
def __init__(self, message: str, status_code: int):
|
|
506
|
+
self.status_code = status_code
|
|
507
|
+
super().__init__(f"[{status_code}] {message}")
|
|
508
|
+
|
|
509
|
+
class NotFoundError(APIError):
|
|
510
|
+
"""Resource not found."""
|
|
511
|
+
|
|
512
|
+
def __init__(self, resource: str, resource_id: str):
|
|
513
|
+
self.resource = resource
|
|
514
|
+
self.resource_id = resource_id
|
|
515
|
+
super().__init__(
|
|
516
|
+
f"{resource} not found: {resource_id}",
|
|
517
|
+
status_code=404,
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
class UnauthorizedError(APIError):
|
|
521
|
+
"""Unauthorized access."""
|
|
522
|
+
|
|
523
|
+
def __init__(self, message: str = "Unauthorized"):
|
|
524
|
+
super().__init__(message, status_code=401)
|
|
525
|
+
|
|
526
|
+
# Usage - Catch specific or broad exceptions
|
|
527
|
+
try:
|
|
528
|
+
user = get_user(user_id)
|
|
529
|
+
except NotFoundError as e:
|
|
530
|
+
# Handle specific error
|
|
531
|
+
logger.warning(f"User not found: {e.resource_id}")
|
|
532
|
+
except APIError as e:
|
|
533
|
+
# Handle any API error
|
|
534
|
+
logger.error(f"API error: {e}")
|
|
535
|
+
except AppError as e:
|
|
536
|
+
# Handle any application error
|
|
537
|
+
logger.error(f"Application error: {e}")
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Rich Exception Information
|
|
541
|
+
|
|
542
|
+
```python
|
|
543
|
+
from typing import Any, Optional
|
|
544
|
+
from dataclasses import dataclass
|
|
545
|
+
|
|
546
|
+
@dataclass
|
|
547
|
+
class ErrorContext:
|
|
548
|
+
"""Context information for errors."""
|
|
549
|
+
operation: str
|
|
550
|
+
user_id: Optional[int] = None
|
|
551
|
+
request_id: Optional[str] = None
|
|
552
|
+
metadata: dict[str, Any] = None
|
|
553
|
+
|
|
554
|
+
def __post_init__(self):
|
|
555
|
+
if self.metadata is None:
|
|
556
|
+
self.metadata = {}
|
|
557
|
+
|
|
558
|
+
class OperationError(Exception):
|
|
559
|
+
"""Exception with rich context information."""
|
|
560
|
+
|
|
561
|
+
def __init__(self, message: str, context: ErrorContext):
|
|
562
|
+
self.message = message
|
|
563
|
+
self.context = context
|
|
564
|
+
super().__init__(self.message)
|
|
565
|
+
|
|
566
|
+
def __str__(self) -> str:
|
|
567
|
+
ctx = self.context
|
|
568
|
+
parts = [f"OperationError: {self.message}"]
|
|
569
|
+
parts.append(f" Operation: {ctx.operation}")
|
|
570
|
+
if ctx.user_id:
|
|
571
|
+
parts.append(f" User ID: {ctx.user_id}")
|
|
572
|
+
if ctx.request_id:
|
|
573
|
+
parts.append(f" Request ID: {ctx.request_id}")
|
|
574
|
+
if ctx.metadata:
|
|
575
|
+
parts.append(f" Metadata: {ctx.metadata}")
|
|
576
|
+
return "\n".join(parts)
|
|
577
|
+
|
|
578
|
+
# Usage
|
|
579
|
+
def process_payment(user_id: int, amount: float, request_id: str) -> None:
|
|
580
|
+
"""Process payment with rich error context."""
|
|
581
|
+
context = ErrorContext(
|
|
582
|
+
operation="process_payment",
|
|
583
|
+
user_id=user_id,
|
|
584
|
+
request_id=request_id,
|
|
585
|
+
metadata={"amount": amount},
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
try:
|
|
589
|
+
charge_card(user_id, amount)
|
|
590
|
+
except CardDeclinedError as e:
|
|
591
|
+
raise OperationError("Payment declined", context) from e
|
|
592
|
+
except InsufficientFundsError as e:
|
|
593
|
+
raise OperationError("Insufficient funds", context) from e
|
|
594
|
+
```
|
|
595
|
+
|
|
127
596
|
## Exception Chaining
|
|
128
597
|
|
|
598
|
+
Exception chaining preserves the original exception context, making debugging easier.
|
|
599
|
+
|
|
600
|
+
### Using 'from' for Chaining
|
|
601
|
+
|
|
129
602
|
```python
|
|
130
|
-
# Preserve original exception
|
|
603
|
+
# Good - Preserve original exception with 'from'
|
|
131
604
|
try:
|
|
132
605
|
result = process_data(data)
|
|
133
606
|
except ValueError as e:
|
|
134
607
|
raise ProcessingError("Failed to process data") from e
|
|
608
|
+
# Traceback will show both ProcessingError and original ValueError
|
|
135
609
|
|
|
136
|
-
#
|
|
610
|
+
# Good - Explicit chaining with context
|
|
611
|
+
try:
|
|
612
|
+
user_data = json.loads(raw_data)
|
|
613
|
+
except json.JSONDecodeError as e:
|
|
614
|
+
raise ValidationError(
|
|
615
|
+
f"Invalid JSON in user data: {e.msg}"
|
|
616
|
+
) from e
|
|
617
|
+
|
|
618
|
+
# Rare - Suppress original exception with 'from None'
|
|
137
619
|
try:
|
|
138
620
|
result = process_data(data)
|
|
139
621
|
except ValueError:
|
|
622
|
+
# Only use 'from None' when original exception is not relevant
|
|
140
623
|
raise ProcessingError("Failed to process data") from None
|
|
141
624
|
```
|
|
142
625
|
|
|
626
|
+
### Implicit Chaining
|
|
627
|
+
|
|
628
|
+
```python
|
|
629
|
+
# Implicit chaining - exception raised during exception handling
|
|
630
|
+
try:
|
|
631
|
+
result = process_data(data)
|
|
632
|
+
except ValueError as e:
|
|
633
|
+
# If log_error raises an exception, both will be in traceback
|
|
634
|
+
log_error(e)
|
|
635
|
+
raise ProcessingError("Failed to process data")
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
### Accessing Exception Chain
|
|
639
|
+
|
|
640
|
+
```python
|
|
641
|
+
try:
|
|
642
|
+
try:
|
|
643
|
+
risky_operation()
|
|
644
|
+
except ValueError as e:
|
|
645
|
+
raise ProcessingError("Processing failed") from e
|
|
646
|
+
except ProcessingError as e:
|
|
647
|
+
# Access the original exception
|
|
648
|
+
original = e.__cause__ # The exception after 'from'
|
|
649
|
+
context = e.__context__ # The exception being handled when this was raised
|
|
650
|
+
|
|
651
|
+
logger.error(f"Processing error: {e}")
|
|
652
|
+
logger.error(f"Original error: {original}")
|
|
653
|
+
```
|
|
654
|
+
|
|
143
655
|
## Logging Exceptions
|
|
144
656
|
|
|
657
|
+
Proper exception logging is crucial for debugging and monitoring.
|
|
658
|
+
|
|
659
|
+
### Basic Exception Logging
|
|
660
|
+
|
|
145
661
|
```python
|
|
146
662
|
import logging
|
|
147
663
|
|
|
148
664
|
logger = logging.getLogger(__name__)
|
|
149
665
|
|
|
150
|
-
# Log exception with traceback
|
|
666
|
+
# Log exception with full traceback
|
|
151
667
|
try:
|
|
152
668
|
result = risky_operation()
|
|
153
669
|
except Exception as e:
|
|
154
|
-
logger.exception("Operation failed") # Includes traceback
|
|
155
|
-
raise
|
|
670
|
+
logger.exception("Operation failed") # Includes full traceback
|
|
671
|
+
raise # Re-raise after logging
|
|
156
672
|
|
|
157
673
|
# Log without traceback
|
|
158
674
|
try:
|
|
159
675
|
result = risky_operation()
|
|
160
676
|
except ValueError as e:
|
|
161
|
-
logger.error(f"Invalid value: {e}")
|
|
677
|
+
logger.error(f"Invalid value: {e}") # No traceback
|
|
678
|
+
result = default_value
|
|
679
|
+
|
|
680
|
+
# Log with different levels
|
|
681
|
+
try:
|
|
682
|
+
result = optional_operation()
|
|
683
|
+
except FileNotFoundError:
|
|
684
|
+
logger.warning("Optional file not found, using defaults")
|
|
162
685
|
result = default_value
|
|
686
|
+
except PermissionError as e:
|
|
687
|
+
logger.error(f"Permission denied: {e}")
|
|
688
|
+
raise
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
### Structured Logging
|
|
692
|
+
|
|
693
|
+
```python
|
|
694
|
+
import logging
|
|
695
|
+
from typing import Any
|
|
696
|
+
|
|
697
|
+
logger = logging.getLogger(__name__)
|
|
698
|
+
|
|
699
|
+
# Log with structured data
|
|
700
|
+
try:
|
|
701
|
+
process_user_data(user_id, data)
|
|
702
|
+
except ValidationError as e:
|
|
703
|
+
logger.error(
|
|
704
|
+
"Validation failed",
|
|
705
|
+
extra={
|
|
706
|
+
"user_id": user_id,
|
|
707
|
+
"error_type": type(e).__name__,
|
|
708
|
+
"error_message": str(e),
|
|
709
|
+
},
|
|
710
|
+
)
|
|
711
|
+
raise
|
|
712
|
+
|
|
713
|
+
# Log with exception info without traceback
|
|
714
|
+
try:
|
|
715
|
+
result = risky_operation()
|
|
716
|
+
except ValueError as e:
|
|
717
|
+
logger.error(
|
|
718
|
+
"Operation failed: %s",
|
|
719
|
+
e,
|
|
720
|
+
exc_info=False, # Don't include traceback
|
|
721
|
+
)
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
### Custom Exception Logging
|
|
725
|
+
|
|
726
|
+
```python
|
|
727
|
+
class LoggingException(Exception):
|
|
728
|
+
"""Exception that logs itself when raised."""
|
|
729
|
+
|
|
730
|
+
def __init__(self, message: str, level: int = logging.ERROR):
|
|
731
|
+
self.message = message
|
|
732
|
+
self.level = level
|
|
733
|
+
super().__init__(self.message)
|
|
734
|
+
|
|
735
|
+
# Log when exception is created
|
|
736
|
+
logger = logging.getLogger(self.__class__.__module__)
|
|
737
|
+
logger.log(self.level, f"{self.__class__.__name__}: {message}")
|
|
738
|
+
|
|
739
|
+
# Usage
|
|
740
|
+
def process_data(data: dict[str, Any]) -> None:
|
|
741
|
+
if not data:
|
|
742
|
+
raise LoggingException("Empty data received", level=logging.WARNING)
|
|
163
743
|
```
|
|
164
744
|
|
|
165
745
|
## Best Practices
|
|
166
746
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
747
|
+
### DO
|
|
748
|
+
|
|
749
|
+
✅ **Catch specific exceptions** - Never use bare `except:` or catch `Exception` unless absolutely necessary
|
|
750
|
+
|
|
751
|
+
✅ **Use context managers** - Prefer `with` statement for resource management
|
|
752
|
+
|
|
753
|
+
✅ **Log exceptions properly** - Use `logger.exception()` to include tracebacks
|
|
754
|
+
|
|
755
|
+
✅ **Create custom exceptions** - For domain-specific errors with clear names
|
|
756
|
+
|
|
757
|
+
✅ **Use exception chaining** - Preserve error context with `from`
|
|
758
|
+
|
|
759
|
+
✅ **Document exceptions** - List raised exceptions in docstrings
|
|
760
|
+
|
|
761
|
+
✅ **Fail fast** - Don't catch exceptions you can't handle
|
|
762
|
+
|
|
763
|
+
✅ **Clean up resources** - Use `finally` or context managers
|
|
764
|
+
|
|
765
|
+
✅ **Use contextlib utilities** - `suppress`, `ExitStack`, etc. for cleaner code
|
|
175
766
|
|
|
176
|
-
|
|
767
|
+
✅ **Create exception hierarchies** - For related errors
|
|
768
|
+
|
|
769
|
+
### DON'T
|
|
770
|
+
|
|
771
|
+
❌ **Don't use bare except** - Catches SystemExit, KeyboardInterrupt, etc.
|
|
177
772
|
|
|
178
773
|
```python
|
|
179
|
-
#
|
|
774
|
+
# Bad
|
|
180
775
|
try:
|
|
181
776
|
do_something()
|
|
182
|
-
except:
|
|
777
|
+
except: # Catches everything, including KeyboardInterrupt
|
|
183
778
|
pass
|
|
184
779
|
|
|
185
|
-
#
|
|
780
|
+
# Good
|
|
781
|
+
try:
|
|
782
|
+
do_something()
|
|
783
|
+
except (ValueError, TypeError) as e:
|
|
784
|
+
logger.error(f"Expected error: {e}")
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
❌ **Don't silence exceptions** - Always log or handle them
|
|
788
|
+
|
|
789
|
+
```python
|
|
790
|
+
# Bad
|
|
186
791
|
try:
|
|
187
792
|
critical_operation()
|
|
188
793
|
except Exception:
|
|
189
|
-
|
|
794
|
+
pass # Silent failure
|
|
190
795
|
|
|
191
|
-
#
|
|
796
|
+
# Good
|
|
797
|
+
try:
|
|
798
|
+
critical_operation()
|
|
799
|
+
except Exception as e:
|
|
800
|
+
logger.exception("Critical operation failed")
|
|
801
|
+
raise
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
❌ **Don't use exceptions for flow control** - Use appropriate methods
|
|
805
|
+
|
|
806
|
+
```python
|
|
807
|
+
# Bad - Using exception for flow control
|
|
192
808
|
try:
|
|
193
809
|
value = my_dict[key]
|
|
194
810
|
except KeyError:
|
|
195
811
|
value = default
|
|
196
812
|
|
|
197
|
-
#
|
|
813
|
+
# Good - Use dict.get()
|
|
198
814
|
value = my_dict.get(key, default)
|
|
815
|
+
|
|
816
|
+
# Bad - Using exception for flow control
|
|
817
|
+
try:
|
|
818
|
+
index = my_list.index(item)
|
|
819
|
+
except ValueError:
|
|
820
|
+
index = -1
|
|
821
|
+
|
|
822
|
+
# Good - Use 'in' operator
|
|
823
|
+
index = my_list.index(item) if item in my_list else -1
|
|
199
824
|
```
|
|
200
825
|
|
|
201
|
-
|
|
826
|
+
❌ **Don't catch Exception without re-raising** - Unless you can truly handle it
|
|
202
827
|
|
|
203
828
|
```python
|
|
204
|
-
#
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
829
|
+
# Bad
|
|
830
|
+
try:
|
|
831
|
+
critical_operation()
|
|
832
|
+
except Exception:
|
|
833
|
+
print("Error occurred") # Swallows the exception
|
|
208
834
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
835
|
+
# Good
|
|
836
|
+
try:
|
|
837
|
+
critical_operation()
|
|
838
|
+
except Exception as e:
|
|
839
|
+
logger.exception("Critical operation failed")
|
|
840
|
+
raise # Re-raise after logging
|
|
841
|
+
```
|
|
212
842
|
|
|
213
|
-
|
|
214
|
-
"""Database connection errors"""
|
|
215
|
-
pass
|
|
843
|
+
❌ **Don't lose exception context** - Use exception chaining
|
|
216
844
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
845
|
+
```python
|
|
846
|
+
# Bad - Loses original exception
|
|
847
|
+
try:
|
|
848
|
+
process_data(data)
|
|
849
|
+
except ValueError:
|
|
850
|
+
raise ProcessingError("Failed") # Original error is lost
|
|
851
|
+
|
|
852
|
+
# Good - Preserves original exception
|
|
853
|
+
try:
|
|
854
|
+
process_data(data)
|
|
855
|
+
except ValueError as e:
|
|
856
|
+
raise ProcessingError("Failed") from e
|
|
220
857
|
```
|
|
221
858
|
|
|
859
|
+
## Advanced Patterns
|
|
860
|
+
|
|
861
|
+
### Exception Groups (Python 3.11+)
|
|
862
|
+
|
|
863
|
+
```python
|
|
864
|
+
# Raise multiple exceptions at once
|
|
865
|
+
raise ExceptionGroup("Multiple errors occurred", [
|
|
866
|
+
ValueError("Invalid value"),
|
|
867
|
+
TypeError("Invalid type"),
|
|
868
|
+
])
|
|
869
|
+
|
|
870
|
+
# Catch exception groups
|
|
871
|
+
try:
|
|
872
|
+
raise ExceptionGroup("errors", [ValueError("bad"), TypeError("wrong")])
|
|
873
|
+
except* ValueError as eg:
|
|
874
|
+
# Handle all ValueError instances
|
|
875
|
+
for e in eg.exceptions:
|
|
876
|
+
logger.error(f"Value error: {e}")
|
|
877
|
+
except* TypeError as eg:
|
|
878
|
+
# Handle all TypeError instances
|
|
879
|
+
for e in eg.exceptions:
|
|
880
|
+
logger.error(f"Type error: {e}")
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
### Retry Logic with Exceptions
|
|
884
|
+
|
|
885
|
+
```python
|
|
886
|
+
from typing import TypeVar, Callable
|
|
887
|
+
import time
|
|
888
|
+
|
|
889
|
+
T = TypeVar('T')
|
|
890
|
+
|
|
891
|
+
def retry(
|
|
892
|
+
func: Callable[..., T],
|
|
893
|
+
max_attempts: int = 3,
|
|
894
|
+
delay: float = 1.0,
|
|
895
|
+
exceptions: tuple[type[Exception], ...] = (Exception,),
|
|
896
|
+
) -> T:
|
|
897
|
+
"""Retry function on exception.
|
|
898
|
+
|
|
899
|
+
Args:
|
|
900
|
+
func: Function to retry
|
|
901
|
+
max_attempts: Maximum number of attempts
|
|
902
|
+
delay: Delay between attempts in seconds
|
|
903
|
+
exceptions: Tuple of exceptions to catch
|
|
904
|
+
|
|
905
|
+
Returns:
|
|
906
|
+
Result of successful function call
|
|
907
|
+
|
|
908
|
+
Raises:
|
|
909
|
+
Last exception if all attempts fail
|
|
910
|
+
"""
|
|
911
|
+
last_exception = None
|
|
912
|
+
|
|
913
|
+
for attempt in range(max_attempts):
|
|
914
|
+
try:
|
|
915
|
+
return func()
|
|
916
|
+
except exceptions as e:
|
|
917
|
+
last_exception = e
|
|
918
|
+
logger.warning(
|
|
919
|
+
f"Attempt {attempt + 1}/{max_attempts} failed: {e}"
|
|
920
|
+
)
|
|
921
|
+
if attempt < max_attempts - 1:
|
|
922
|
+
time.sleep(delay)
|
|
923
|
+
|
|
924
|
+
# All attempts failed
|
|
925
|
+
raise last_exception
|
|
926
|
+
|
|
927
|
+
# Usage
|
|
928
|
+
result = retry(
|
|
929
|
+
lambda: fetch_data_from_api(),
|
|
930
|
+
max_attempts=3,
|
|
931
|
+
delay=2.0,
|
|
932
|
+
exceptions=(ConnectionError, TimeoutError),
|
|
933
|
+
)
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
### Validation with Multiple Errors
|
|
937
|
+
|
|
938
|
+
```python
|
|
939
|
+
from typing import List
|
|
940
|
+
|
|
941
|
+
class ValidationErrors(Exception):
|
|
942
|
+
"""Exception containing multiple validation errors."""
|
|
943
|
+
|
|
944
|
+
def __init__(self, errors: List[str]):
|
|
945
|
+
self.errors = errors
|
|
946
|
+
super().__init__(f"{len(errors)} validation errors")
|
|
947
|
+
|
|
948
|
+
def __str__(self) -> str:
|
|
949
|
+
return "\n".join([
|
|
950
|
+
f"Validation failed with {len(self.errors)} errors:",
|
|
951
|
+
*[f" - {error}" for error in self.errors],
|
|
952
|
+
])
|
|
953
|
+
|
|
954
|
+
def validate_user(user_data: dict) -> None:
|
|
955
|
+
"""Validate user data, collecting all errors.
|
|
956
|
+
|
|
957
|
+
Args:
|
|
958
|
+
user_data: User data to validate
|
|
959
|
+
|
|
960
|
+
Raises:
|
|
961
|
+
ValidationErrors: If validation fails
|
|
962
|
+
"""
|
|
963
|
+
errors = []
|
|
964
|
+
|
|
965
|
+
if not user_data.get("email"):
|
|
966
|
+
errors.append("Email is required")
|
|
967
|
+
elif "@" not in user_data["email"]:
|
|
968
|
+
errors.append("Email must contain @")
|
|
969
|
+
|
|
970
|
+
if not user_data.get("username"):
|
|
971
|
+
errors.append("Username is required")
|
|
972
|
+
elif len(user_data["username"]) < 3:
|
|
973
|
+
errors.append("Username must be at least 3 characters")
|
|
974
|
+
|
|
975
|
+
if not user_data.get("age"):
|
|
976
|
+
errors.append("Age is required")
|
|
977
|
+
elif user_data["age"] < 18:
|
|
978
|
+
errors.append("User must be at least 18 years old")
|
|
979
|
+
|
|
980
|
+
if errors:
|
|
981
|
+
raise ValidationErrors(errors)
|
|
982
|
+
|
|
983
|
+
# Usage
|
|
984
|
+
try:
|
|
985
|
+
validate_user({"email": "invalid", "username": "ab"})
|
|
986
|
+
except ValidationErrors as e:
|
|
987
|
+
logger.error(str(e))
|
|
988
|
+
# Validation failed with 3 errors:
|
|
989
|
+
# - Email must contain @
|
|
990
|
+
# - Username must be at least 3 characters
|
|
991
|
+
# - Age is required
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
## Summary
|
|
995
|
+
|
|
996
|
+
**Key Takeaways:**
|
|
997
|
+
|
|
998
|
+
1. **Always catch specific exceptions** - Never use bare `except:`
|
|
999
|
+
2. **Use context managers** - For automatic resource cleanup
|
|
1000
|
+
3. **Create custom exceptions** - With clear hierarchies for domain errors
|
|
1001
|
+
4. **Use contextlib utilities** - `@contextmanager`, `suppress`, `ExitStack`
|
|
1002
|
+
5. **Chain exceptions** - Preserve error context with `from`
|
|
1003
|
+
6. **Log exceptions properly** - Use `logger.exception()` for tracebacks
|
|
1004
|
+
7. **Document exceptions** - In docstrings with `Raises:` section
|
|
1005
|
+
8. **Don't use exceptions for flow control** - Use appropriate methods instead
|
|
1006
|
+
9. **Fail fast** - Don't catch exceptions you can't handle
|
|
1007
|
+
10. **Clean up resources** - Use `finally` or context managers
|
|
1008
|
+
|