|
| 1 | +--- |
| 2 | +alwaysApply: true |
| 3 | +--- |
| 4 | + |
| 5 | +## Python |
| 6 | + |
| 7 | +- **Imports ALWAYS go to the top of the file - NEVER import within functions, unless it would |
| 8 | + create circular import** ⚠️ |
| 9 | +- Use Decimal for financial calculations - never use float for money/prices |
| 10 | +- Don't abbreviate packages (use "pathlib" not "pl", "yaml" not "yml") |
| 11 | + |
| 12 | +## Modern python |
| 13 | + |
| 14 | +We use python 3.13+ and follow modern best practices, including: |
| 15 | + |
| 16 | +- Use `Path` lib for files instead of `open` |
| 17 | +- Use `var!s` instead of `str(var)` |
| 18 | +- Prefer walrus operator (:=) to reduce repetition in code |
| 19 | +- Use modern union syntax `X | Y` instead of `Union[X, Y]` or `(X, Y)` in isinstance calls |
| 20 | + |
| 21 | +## Libraries to use |
| 22 | + |
| 23 | +- Use `click` for command line applications with rich styling |
| 24 | +- Use `rich` for making command line applications more beautiful and more user friendly |
| 25 | +- We have `pydantic` installed (through language model libraries), so you may use that when it adds value |
| 26 | +- We are using `httpx` for async HTTP requests |
| 27 | +- Use `loguru` for logging (from `aicodebot.helpers` as `logger`) |
| 28 | + |
| 29 | +## Exceptions - READ THIS CAREFULLY 🚨 |
| 30 | + |
| 31 | +### ⚠️ CRITICAL: try/except is FORBIDDEN except in these EXACT 2 scenarios: |
| 32 | + |
| 33 | +**SCENARIO 1: Handling a SPECIFIC exception type with actual handling logic** |
| 34 | + |
| 35 | +```python |
| 36 | +# ✅ ALLOWED: Specific exception + real handling + proper logging |
| 37 | +try: |
| 38 | + result = process_api_request(data) |
| 39 | +except APIRateLimitError as e: |
| 40 | + logger.warning(f"Rate limited, retrying in {e.retry_after} seconds") |
| 41 | + time.sleep(e.retry_after) |
| 42 | + return retry_request(data) |
| 43 | +except APIAuthError as e: |
| 44 | + logger.error(f"Authentication failed: {e}") |
| 45 | + raise ConfigurationError("Invalid API key - run 'aicodebot configure'") from e |
| 46 | +``` |
| 47 | + |
| 48 | +**SCENARIO 2: Processing loop where you want to continue on errors** |
| 49 | + |
| 50 | +```python |
| 51 | +# ✅ ALLOWED: Loop processing where individual failures shouldn't stop the loop |
| 52 | +for file_path in file_paths: |
| 53 | + try: |
| 54 | + process_file(file_path) |
| 55 | + except FileProcessingError as e: |
| 56 | + logger.warning(f"Failed to process {file_path}: {e}") |
| 57 | + continue # Continue processing other files |
| 58 | +``` |
| 59 | + |
| 60 | +### 🚫 ABSOLUTELY FORBIDDEN - NEVER EVER DO THESE: |
| 61 | + |
| 62 | +```python |
| 63 | +# 🚫 NEVER: Generic exception catching |
| 64 | +try: |
| 65 | + some_function() |
| 66 | +except Exception as e: |
| 67 | + logger.error("Something went wrong") # This hides errors from us! |
| 68 | + |
| 69 | +# 🚫 NEVER: Log and continue without specific handling |
| 70 | +try: |
| 71 | + some_function() |
| 72 | +except SomeError as e: |
| 73 | + logger.error(f"Error: {e}") # Just logging is NOT handling! |
| 74 | + |
| 75 | +# 🚫 NEVER: Swallowing exceptions |
| 76 | +try: |
| 77 | + some_function() |
| 78 | +except Exception: |
| 79 | + pass # Absolutely forbidden! |
| 80 | +``` |
| 81 | + |
| 82 | +### ✅ DEFAULT APPROACH - 99% of the time, do this: |
| 83 | + |
| 84 | +```python |
| 85 | +# ✅ PREFERRED: Let exceptions bubble up |
| 86 | +result = process_code_changes(files) # No try/except at all! |
| 87 | +model = get_language_model(provider) # Let it crash if provider is invalid! |
| 88 | +response = httpx.get(url) # Let network errors bubble up! |
| 89 | +``` |
| 90 | + |
| 91 | +### 🎯 REMEMBER: |
| 92 | + |
| 93 | +- **Exceptions are NOT errors to be hidden - they're signals something needs attention** |
| 94 | +- **If you're tempted to use try/except, ask: "Am I actually HANDLING this or just HIDING |
| 95 | + it?"** |
| 96 | +- **Logging an error is NOT the same as handling an error** |
| 97 | + |
| 98 | +## 🚫 Defensive Programming - hasattr/getattr |
| 99 | + |
| 100 | +**NEVER use hasattr() or getattr() unless there's a REALLY good reason.** |
| 101 | + |
| 102 | +### ❌ BAD: Defensive programming |
| 103 | + |
| 104 | +```python |
| 105 | +# 🚫 NEVER: Checking if methods exist in your own class |
| 106 | +if hasattr(self, "process_commit") and callable(self.process_commit): |
| 107 | + self.process_commit(commit_data) |
| 108 | + |
| 109 | +# 🚫 NEVER: Using getattr with defaults for config |
| 110 | +value = getattr(config, "SOME_SETTING", "default") |
| 111 | +``` |
| 112 | + |
| 113 | +### ✅ GOOD: Trust your code |
| 114 | + |
| 115 | +```python |
| 116 | +# ✅ PREFERRED: Just call the method - it exists! |
| 117 | +self.process_commit(commit_data) |
| 118 | + |
| 119 | +# ✅ PREFERRED: Config entries exist - don't use defaults |
| 120 | +value = config.SOME_SETTING |
| 121 | +``` |
| 122 | + |
| 123 | +### 🎯 PHILOSOPHY: |
| 124 | + |
| 125 | +- **Trust your own classes** - Methods you defined exist |
| 126 | +- **Fail fast** - If something doesn't exist, let it crash |
| 127 | +- **Broken is better than wrong** - Don't hide missing attributes with defaults |
| 128 | + |
| 129 | +## Logging |
| 130 | + |
| 131 | +Use our logger which is loguru, from `aicodebot.helpers.logger`. Use emojis when they are |
| 132 | +helpful. Use other log levels from loguru when helpful, such as logger.success() when a command |
| 133 | +completes. Add logging that is helpful not only for humans, but for AI to troubleshoot. Always |
| 134 | +use logger.exception in an exception catch, not logger.error() |
| 135 | + |
| 136 | +```python |
| 137 | +from aicodebot.helpers import logger |
| 138 | +logger.info("🚀 Starting code review process", extra={"file_count": len(files)}) |
| 139 | +``` |
0 commit comments